Example #1
0
def check_base_action(request=None, action=None, anonymous=False):
    """
    This decorator function takes the request and verifies the given action
    for the SCOPE ADMIN or USER.
    :param request:
    :param action:
    :param anonymous: If set to True, the user data is taken from the request
        parameters.
    :return: True otherwise raises an Exception
    """
    ERROR = {
        "user":
        "******"
        "allowed!" % action,
        "admin":
        "Admin actions are defined, but the action %s is not "
        "allowed!" % action
    }
    params = request.all_data
    policy_object = g.policy_object
    username = g.logged_in_user.get("username")
    role = g.logged_in_user.get("role")
    scope = SCOPE.ADMIN
    admin_realm = g.logged_in_user.get("realm")
    realm = params.get("realm")
    resolver = params.get("resolver")
    if type(realm) == list and len(realm) == 1:
        realm = realm[0]

    if role == ROLE.USER:
        scope = SCOPE.USER
        # Reset the admin realm
        admin_realm = None
        realm = realm or g.logged_in_user.get("realm")

    # In certain cases we can not resolve the user by the serial!
    if action not in [ACTION.AUDIT]:
        # get the realm by the serial:
        if not realm and params.get("serial"):
            realm = get_realms_of_token(params.get("serial"),
                                        only_first_realm=True)

        # get the realm by the serial, while the serial is part of the URL like
        # DELETE /token/serial
        if not realm and request.view_args and request.view_args.get("serial"):
            realm = get_realms_of_token(request.view_args.get("serial"),
                                        only_first_realm=True)

    action = policy_object.get_policies(action=action,
                                        user=username,
                                        realm=realm,
                                        scope=scope,
                                        resolver=resolver,
                                        client=g.client_ip,
                                        adminrealm=admin_realm,
                                        active=True)
    action_at_all = policy_object.get_policies(scope=scope,
                                               active=True,
                                               all_times=True)
    if action_at_all and len(action) == 0:
        raise PolicyError(ERROR.get(role))
    return True
Example #2
0
def auth_user_timelimit(wrapped_function, user_object, passw, options=None):
    """
    This decorator checks the policy settings of
    ACTION.AUTHMAXSUCCESS,
    ACTION.AUTHMAXFAIL
    If the authentication was successful, it checks, if the number of allowed
    successful authentications is exceeded (AUTHMAXSUCCESS).

    If the AUTHMAXFAIL is exceed it denies even a successful authentication.

    The wrapped function is usually token.check_user_pass, which takes the
    arguments (user, passw, options={})

    :param wrapped_function:
    :param user_object:
    :param passw:
    :param options: Dict containing values for "g" and "clientip"
    :return: Tuple of True/False and reply-dictionary
    """
    # First we call the wrapped function
    res, reply_dict = wrapped_function(user_object, passw, options)

    options = options or {}
    g = options.get("g")
    if g:

        clientip = options.get("clientip")
        policy_object = g.policy_object

        max_success = policy_object.get_action_values(
            action=ACTION.AUTHMAXSUCCESS,
            scope=SCOPE.AUTHZ,
            realm=user_object.realm,
            resolver=user_object.resolver,
            user=user_object.login,
            client=clientip)
        max_fail = policy_object.get_action_values(
            action=ACTION.AUTHMAXFAIL,
            scope=SCOPE.AUTHZ,
            realm=user_object.realm,
            resolver=user_object.resolver,
            user=user_object.login,
            client=clientip)
        # Check for maximum failed authentications
        # Always - also in case of unsuccessful authentication
        if len(max_fail) > 1:
            raise PolicyError("Contradicting policies for {0!s}".format(
                ACTION.AUTHMAXFAIL))
        if len(max_fail) == 1:
            policy_count, tdelta = parse_timelimit(max_fail[0])
            fail_c = g.audit_object.get_count(
                {
                    "user": user_object.login,
                    "realm": user_object.realm,
                    "action": "%/validate/check"
                },
                success=False,
                timedelta=tdelta)
            log.debug("Checking users timelimit %s: %s "
                      "failed authentications" % (max_fail[0], fail_c))
            if fail_c >= policy_count:
                res = False
                reply_dict["message"] = ("Only %s failed authentications "
                                         "per %s" % (policy_count, tdelta))

        if res:
            # Check for maximum successful authentications
            # Only in case of a successful authentication
            if len(max_success) > 1:
                raise PolicyError("Contradicting policies for {0!s}".format(
                    ACTION.AUTHMAXSUCCESS))

            if len(max_success) == 1:
                policy_count, tdelta = parse_timelimit(max_success[0])
                # check the successful authentications for this user
                succ_c = g.audit_object.get_count(
                    {
                        "user": user_object.login,
                        "realm": user_object.realm,
                        "action": "%/validate/check"
                    },
                    success=True,
                    timedelta=tdelta)
                log.debug("Checking users timelimit %s: %s "
                          "succesful authentications" %
                          (max_success[0], succ_c))
                if succ_c >= policy_count:
                    res = False
                    reply_dict["message"] = ("Only %s successfull "
                                             "authentications per %s" %
                                             (policy_count, tdelta))

    return res, reply_dict
Example #3
0
def config_lost_token(wrapped_function, *args, **kwds):
    """
    Decorator to decorate the lib.token.lost_token function.
    Depending on ACTION.LOSTTOKENVALID, ACTION.LOSTTOKENPWCONTENTS,
    ACTION.LOSTTOKENPWLEN it sets the check_otp parameter, to signal
    how the lostToken should be generated.

    :param wrapped_function: Usually the function lost_token()
    :param args: argument "serial" as the old serial number
    :param kwds: keyword arguments like "validity", "contents", "pw_len"
    kwds["options"] contains the flask g

    :return: calls the original function with the modified "validity",
    "contents" and "pw_len" argument
    """
    # if called in any other way, options may be None
    #  or it might have no element "g".
    from privacyidea.lib.token import get_tokens
    options = kwds.get("options") or {}
    g = options.get("g")
    if g:
        # We need the old serial number, to determine the user - if it exist.
        serial = args[0]
        toks = get_tokens(serial=serial)
        if len(toks) == 1:
            username = None
            realm = None
            user_object = toks[0].user
            if user_object:
                username = user_object.login
                realm = user_object.realm
            clientip = options.get("clientip")
            # get the policy
            policy_object = g.policy_object
            contents_list = policy_object.get_action_values(
                ACTION.LOSTTOKENPWCONTENTS,
                scope=SCOPE.ENROLL,
                realm=realm,
                user=username,
                client=clientip)
            validity_list = policy_object.get_action_values(
                ACTION.LOSTTOKENVALID,
                scope=SCOPE.ENROLL,
                realm=realm,
                user=username,
                client=clientip)
            pw_len_list = policy_object.get_action_values(
                ACTION.LOSTTOKENPWLEN,
                scope=SCOPE.ENROLL,
                realm=realm,
                user=username,
                client=clientip)

            if contents_list:
                if len(contents_list) > 1:  # pragma: no cover
                    # We can not decide how to handle the request, so we raise an
                    # exception
                    raise PolicyError("There are contradicting policies for the "
                                      "action %s" % ACTION.LOSTTOKENPWCONTENTS)
                kwds["contents"] = contents_list[0]

            if validity_list:
                if len(validity_list) > 1:  # pragma: no cover
                    # We can not decide how to handle the request, so we raise an
                    # exception
                    raise PolicyError("There are contradicting policies for the "
                                      "action %s" % ACTION.LOSTTOKENVALID)
                kwds["validity"] = int(validity_list[0])

            if pw_len_list:
                if len(pw_len_list) > 1:  # pragma: no cover
                    # We can not decide how to handle the request, so we raise an
                    # exception
                    raise PolicyError("There are contradicting policies for the "
                                      "action %s" % ACTION.LOSTTOKENPWLEN)
                kwds["pw_len"] = int(pw_len_list[0])

    return wrapped_function(*args, **kwds)
Example #4
0
def check_otp_pin(request=None, action=None):
    """
    This policy function checks if the OTP PIN that is about to be set
    follows the OTP PIN policies ACTION.OTPPINMAXLEN, ACTION.OTPPINMINLEN and
    ACTION.OTPPINCONTENTS in the SCOPE.USER. It is used to decorate the API
    functions.

    The pin is investigated in the params as pin = params.get("pin")

    In case the given OTP PIN does not match the requirements an exception is
    raised.
    """
    # This policy is only used for USER roles at the moment:
    if g.logged_in_user.get("role") == ROLE.USER:
        params = request.all_data
        pin = params.get("otppin", "") or params.get("pin", "")
        serial = params.get("serial")
        if serial:
            # if this is a token, that does not use a pin, we ignore this check
            # And immediately return true
            tokensobject_list = get_tokens(serial=serial)
            if (len(tokensobject_list) == 1
                    and tokensobject_list[0].using_pin is False):
                return True
        policy_object = g.policy_object
        user_object = get_user_from_param(params)
        # get the policies for minimum length, maximum length and PIN contents
        pol_minlen = policy_object.get_action_values(
            action=ACTION.OTPPINMINLEN,
            scope=SCOPE.USER,
            user=user_object.login,
            realm=user_object.realm,
            client=g.client_ip,
            unique=True)
        pol_maxlen = policy_object.get_action_values(
            action=ACTION.OTPPINMAXLEN,
            scope=SCOPE.USER,
            user=user_object.login,
            realm=user_object.realm,
            client=g.client_ip,
            unique=True)
        pol_contents = policy_object.get_action_values(
            action=ACTION.OTPPINCONTENTS,
            scope=SCOPE.USER,
            user=user_object.login,
            realm=user_object.realm,
            client=g.client_ip,
            unique=True)

        if len(pol_minlen) == 1 and len(pin) < int(pol_minlen[0]):
            # check the minimum length requirement
            raise PolicyError("The minimum OTP PIN length is {0!s}".format(
                pol_minlen[0]))

        if len(pol_maxlen) == 1 and len(pin) > int(pol_maxlen[0]):
            # check the maximum length requirement
            raise PolicyError("The maximum OTP PIN length is {0!s}".format(
                pol_minlen[0]))

        if len(pol_contents) == 1:
            # check the contents requirement
            chars = "[a-zA-Z]"  # c
            digits = "[0-9]"  # n
            special = "[.:,;-_<>+*!/()=?$§%&#~\^]"  # s
            no_others = False
            grouping = False

            if pol_contents[0] == "-":
                no_others = True
                pol_contents = pol_contents[1:]
            elif pol_contents[0] == "+":
                grouping = True
                pol_contents = pol_contents[1:]
            #  TODO implement grouping and substraction
            if "c" in pol_contents[0] and not re.search(chars, pin):
                raise PolicyError(
                    "Missing character in PIN: {0!s}".format(chars))
            if "n" in pol_contents[0] and not re.search(digits, pin):
                raise PolicyError(
                    "Missing character in PIN: {0!s}".format(digits))
            if "s" in pol_contents[0] and not re.search(special, pin):
                raise PolicyError(
                    "Missing character in PIN: {0!s}".format(special))

    return True
Example #5
0
    def check_otp(self, otpval, counter=None, window=None, options=None):
        """
        This checks the response of a previous challenge.
        :param otpval: N/A
        :param counter: The authentication counter
        :param window: N/A
        :param options: contains "clientdata", "signaturedata" and
            "transaction_id"
        :return: A value > 0 in case of success
        """
        ret = -1
        clientdata = options.get("clientdata")
        signaturedata = options.get("signaturedata")
        transaction_id = options.get("transaction_id")
        # The challenge in the challenge DB object is saved in hex
        challenge = binascii.unhexlify(options.get("challenge", ""))
        if clientdata and signaturedata and transaction_id and challenge:
            # This is a valid response for a U2F token
            challenge_url = url_encode(challenge)
            clientdata = url_decode(clientdata)
            clientdata_dict = json.loads(clientdata)
            client_challenge = clientdata_dict.get("challenge")
            if challenge_url != client_challenge:
                return ret
            if clientdata_dict.get("typ") != "navigator.id.getAssertion":
                raise ValidateError("Incorrect navigator.id")
            #client_origin = clientdata_dict.get("origin")
            signaturedata = url_decode(signaturedata)
            signaturedata_hex = binascii.hexlify(signaturedata)
            user_presence, counter, signature = parse_response_data(
                signaturedata_hex)

            user_pub_key = self.get_tokeninfo("pubKey")
            app_id = self.get_tokeninfo("appId")
            if check_response(user_pub_key, app_id, clientdata,
                              binascii.hexlify(signature), counter,
                              user_presence):
                # Signature verified.
                # check, if the counter increased!
                if counter > self.get_otp_count():
                    self.set_otp_count(counter)
                    ret = counter
                    # At this point we can check, if the attestation
                    # certificate is authorized.
                    # If not, we can raise a policy exception
                    g = options.get("g")
                    if self.user:
                        token_user = self.user.login
                        token_realm = self.user.realm
                        token_resolver = self.user.resolver
                    else:
                        token_realm = token_resolver = token_user = None
                    allowed_certs_pols = g.policy_object.get_action_values(
                        U2FACTION.REQ,
                        scope=SCOPE.AUTHZ,
                        realm=token_realm,
                        user=token_user,
                        resolver=token_resolver,
                        client=g.client_ip)
                    for allowed_cert in allowed_certs_pols:
                        tag, matching, _rest = allowed_cert.split("/", 3)
                        tag_value = self.get_tokeninfo(
                            "attestation_{0!s}".format(tag))
                        # if we do not get a match, we bail out
                        m = re.search(matching, tag_value)
                        if not m:
                            log.warning(
                                "The U2F device {0!s} is not "
                                "allowed to authenticate due to policy "
                                "restriction".format(self.token.serial))
                            raise PolicyError("The U2F device is not allowed "
                                              "to authenticate due to policy "
                                              "restriction.")

                else:
                    log.warning("The signature of %s was valid, but contained "
                                "an old counter." % self.token.serial)
            else:
                log.warning("Checking response for token {0!s} failed.".format(
                    self.token.serial))

        return ret
Example #6
0
def auth_otppin(wrapped_function, *args, **kwds):
    """
    Decorator to decorate the tokenclass.check_pin function.
    Depending on the ACTION.OTPPIN it
    * either simply accepts an empty pin
    * checks the pin against the userstore
    * or passes the request to the wrapped_function

    :param wrapped_function: In this case the wrapped function should be
    tokenclass.check_ping
    :param *args: args[1] is the pin
    :param **kwds: kwds["options"] contains the flask g
    :return: True or False
    """
    ERROR = "There are contradicting policies for the action {0!s}!".format( \
            ACTION.OTPPIN)
    # if tokenclass.check_pin is called in any other way, options may be None
    #  or it might have no element "g".
    options = kwds.get("options") or {}
    g = options.get("g")
    if g:
        token = args[0]
        pin = args[1]
        clientip = options.get("clientip")
        user_object = kwds.get("user")
        if not user_object:
            # No user in the parameters, so we need to determine the owner of
            #  the token
            user_object = token.user
            realms = token.get_realms()
            if not user_object and len(realms):
                # if the token has not owner, we take a realm.
                user_object = User("", realm=realms[0])
        if not user_object:
            # If we still have no user and no tokenrealm, we create an empty
            # user object.
            user_object=User("", realm="")
        # get the policy
        policy_object = g.policy_object
        otppin_list = policy_object.get_action_values(ACTION.OTPPIN,
                                                      scope=SCOPE.AUTH,
                                                      realm=user_object.realm,
                                                      user=user_object.login,
                                                      client=clientip)
        if otppin_list:
            # There is an otppin policy
            if len(otppin_list) > 1:
                # We can not decide how to handle the request, so we raise an
                # exception
                raise PolicyError(ERROR)

            if otppin_list[0] == ACTIONVALUE.NONE:
                if pin == "":
                    # No PIN checking, we expect an empty PIN!
                    return True
                else:
                    return False

            if otppin_list[0] == ACTIONVALUE.USERSTORE:
                rv = user_object.check_password(pin)
                return rv is not None

    # call and return the original check_pin function
    return wrapped_function(*args, **kwds)
Example #7
0
def check_otp_pin(request=None, action=None):
    """
    This policy function checks if the OTP PIN that is about to be set
    follows the OTP PIN policies ACTION.OTPPINMAXLEN, ACTION.OTPPINMINLEN and
    ACTION.OTPPINCONTENTS and token-type-specific PIN policy actions in the
    SCOPE.USER or SCOPE.ADMIN. It is used to decorate the API functions.

    The pin is investigated in the params as "otppin" or "pin"

    In case the given OTP PIN does not match the requirements an exception is
    raised.
    """
    params = request.all_data
    realm = params.get("realm")
    pin = params.get("otppin", "") or params.get("pin", "")
    serial = params.get("serial")
    tokentype = params.get("type")
    if not serial and action == ACTION.SETPIN:
        path_elems = request.path.split("/")
        serial = path_elems[-1]
        # Also set it for later use
        request.all_data["serial"] = serial
    if serial:
        # if this is a token, that does not use a pin, we ignore this check
        # And immediately return true
        tokensobject_list = get_tokens(serial=serial)
        if len(tokensobject_list) == 1:
            if tokensobject_list[0].using_pin is False:
                return True
            tokentype = tokensobject_list[0].token.tokentype
    # the default tokentype is still HOTP
    tokentype = tokentype or "hotp"
    policy_object = g.policy_object
    role = g.logged_in_user.get("role")
    username = g.logged_in_user.get("username")
    if role == ROLE.ADMIN:
        scope = SCOPE.ADMIN
        admin_realm = g.logged_in_user.get("realm")
        realm = params.get("realm", "")
    else:
        scope = SCOPE.USER
        realm = g.logged_in_user.get("realm")
        admin_realm = None
    # get the policies for minimum length, maximum length and PIN contents
    # first try to get a token specific policy - otherwise fall back to
    # default policy
    pol_minlen = policy_object.get_action_values(
        action="{0!s}_{1!s}".format(tokentype, ACTION.OTPPINMINLEN),
        scope=scope, user=username, realm=realm, adminrealm=admin_realm,
        client=g.client_ip, unique=True) or \
                 policy_object.get_action_values(
                     action=ACTION.OTPPINMINLEN, scope=scope, user=username,
                     realm=realm, adminrealm=admin_realm, client=g.client_ip,
                     unique=True)

    pol_maxlen = policy_object.get_action_values(
        action="{0!s}_{1!s}".format(tokentype, ACTION.OTPPINMAXLEN),
        scope=scope, user=username, realm=realm, adminrealm=admin_realm,
        client=g.client_ip, unique=True) or \
                 policy_object.get_action_values(
                     action=ACTION.OTPPINMAXLEN, scope=scope, user=username,
                     realm=realm, adminrealm=admin_realm, client=g.client_ip,
                     unique=True)

    pol_contents = policy_object.get_action_values(
        action="{0!s}_{1!s}".format(tokentype, ACTION.OTPPINCONTENTS),
        scope=scope, user=username, realm=realm, adminrealm=admin_realm,
        client=g.client_ip, unique=True) or \
                   policy_object.get_action_values(
                       action=ACTION.OTPPINCONTENTS, scope=scope,
                       user=username, realm=realm, adminrealm=admin_realm,
                       client=g.client_ip, unique=True)

    if len(pol_minlen) == 1 and len(pin) < int(pol_minlen[0]):
        # check the minimum length requirement
        raise PolicyError("The minimum OTP PIN length is {0!s}".format(
            pol_minlen[0]))

    if len(pol_maxlen) == 1 and len(pin) > int(pol_maxlen[0]):
        # check the maximum length requirement
        raise PolicyError("The maximum OTP PIN length is {0!s}".format(
            pol_maxlen[0]))

    if len(pol_contents) == 1:
        # check the contents requirement
        chars = "[a-zA-Z]"  # c
        digits = "[0-9]"  # n
        special = "[.:,;_<>+*!/()=?$§%&#~\^-]"  # s
        no_others = False
        grouping = False

        if pol_contents[0] == "-":
            no_others = True
            pol_contents = pol_contents[1:]
        elif pol_contents[0] == "+":
            grouping = True
            pol_contents = pol_contents[1:]
        #  TODO implement grouping and substraction
        if "c" in pol_contents[0] and not re.search(chars, pin):
            raise PolicyError("Missing character in PIN: {0!s}".format(chars))
        if "n" in pol_contents[0] and not re.search(digits, pin):
            raise PolicyError("Missing character in PIN: {0!s}".format(digits))
        if "s" in pol_contents[0] and not re.search(special, pin):
            raise PolicyError(
                "Missing character in PIN: {0!s}".format(special))

    return True
Example #8
0
def twostep_enrollment_activation(request=None, action=None):
    """
    This policy function enables the two-step enrollment process according
    to the configured policies.
    It is used to decorate the ``/token/init`` endpoint.

    If a ``<type>_2step`` policy matches, the ``2stepinit`` parameter is handled according to the policy.
    If no policy matches, the ``2stepinit`` parameter is removed from the request data.
    """
    policy_object = g.policy_object
    user_object = get_user_from_param(request.all_data)
    serial = getParam(request.all_data, "serial", optional)
    token_type = getParam(request.all_data, "type", optional, "hotp")
    token_exists = False
    if serial:
        tokensobject_list = get_tokens(serial=serial)
        if len(tokensobject_list) == 1:
            token_type = tokensobject_list[0].token.tokentype
            token_exists = True
    token_type = token_type.lower()
    role = g.logged_in_user.get("role")
    # Differentiate between an admin enrolling a token for the
    # user and a user self-enrolling a token.
    if role == ROLE.ADMIN:
        scope = SCOPE.ADMIN
        adminrealm = g.logged_in_user.get("realm")
    else:
        scope = SCOPE.USER
        adminrealm = None
    realm = user_object.realm
    # In any case, the policy's user attribute is matched against the
    # currently logged-in user (which may be the admin or the
    # self-enrolling user).
    user = g.logged_in_user.get("username")
    # Tokentypes have separate twostep actions
    action = "{}_2step".format(token_type)
    twostep_enabled_pols = policy_object.get_action_values(
        action=action,
        scope=scope,
        unique=True,
        user=user,
        realm=realm,
        client=g.client_ip,
        adminrealm=adminrealm)
    if twostep_enabled_pols:
        enabled_setting = twostep_enabled_pols[0]
        if enabled_setting == "allow":
            # The user is allowed to pass 2stepinit=1
            pass
        elif enabled_setting == "force":
            # We force 2stepinit to be 1 (if the token does not exist yet)
            if not token_exists:
                request.all_data["2stepinit"] = 1
        else:
            raise PolicyError(
                "Unknown 2step policy setting: {}".format(enabled_setting))
    else:
        # If no policy matches, the user is not allowed
        # to pass 2stepinit
        # Force two-step initialization to be None
        if "2stepinit" in request.all_data:
            del request.all_data["2stepinit"]
    return True
Example #9
0
def u2ftoken_allowed(request, action):
    """
    This is a token specific wrapper for u2f token for the endpoint
     /token/init.
     According to the policy scope=SCOPE.ENROLL,
     action=U2FACTINO.REQ it checks, if the assertion certificate is an 
     allowed U2F token type.

     If the token, which is enrolled contains a non allowed attestation 
     certificate, we bail out.

    :param request: 
    :param action: 
    :return: 
    """
    from privacyidea.lib.tokens.u2ftoken import (U2FACTION,
                                                 parse_registration_data)
    from privacyidea.lib.tokens.u2f import x509name_to_string
    policy_object = g.policy_object
    # Get the registration data of the 2nd step of enrolling a U2F device
    reg_data = request.all_data.get("regdata")
    if reg_data:
        # We have a registered u2f device!
        serial = request.all_data.get("serial")
        user_object = request.User

        attestation_cert, user_pub_key, key_handle, \
        signature, description = parse_registration_data(reg_data)

        cert_info = {
            "attestation_issuer":
            x509name_to_string(attestation_cert.get_issuer()),
            "attestation_serial":
            "{!s}".format(attestation_cert.get_serial_number()),
            "attestation_subject":
            x509name_to_string(attestation_cert.get_subject())
        }

        if user_object:
            token_user = user_object.login
            token_realm = user_object.realm
            token_resolver = user_object.resolver
        else:
            token_realm = token_resolver = token_user = None

        allowed_certs_pols = policy_object.get_action_values(
            U2FACTION.REQ,
            scope=SCOPE.ENROLL,
            realm=token_realm,
            user=token_user,
            resolver=token_resolver,
            client=g.client_ip)
        for allowed_cert in allowed_certs_pols:
            tag, matching, _rest = allowed_cert.split("/", 3)
            tag_value = cert_info.get("attestation_{0!s}".format(tag))
            # if we do not get a match, we bail out
            m = re.search(matching, tag_value)
            if not m:
                log.warning("The U2F device {0!s} is not "
                            "allowed to be registered due to policy "
                            "restriction".format(serial))
                raise PolicyError("The U2F device is not allowed "
                                  "to be registered due to policy "
                                  "restriction.")
                # TODO: Maybe we should delete the token, as it is a not
                # usable U2F token, now.

    return True
Example #10
0
def autoassign(request, response):
    """
    This decorator decorates the function /validate/check.
    Depending on ACTION.AUTOASSIGN it checks if the user has no token and if
    the given OTP-value matches a token in the users realm, that is not yet
    assigned to any user.

    If a token can be found, it assigns the token to the user also taking
    into account ACTION.MAXTOKENUSER and ACTION.MAXTOKENREALM.
    :return:
    """
    content = json.loads(response.data)
    # check, if the authentication was successful, then we need to do nothing
    if content.get("result").get("value") is False:
        user_obj = request.User
        #user_obj = get_user_from_param(request.all_data)
        password = request.all_data.get("pass", "")
        if user_obj.login and user_obj.realm:
            # If there is no user in the request (because it is a serial
            # authentication request) we immediately bail out
            # check if the policy is defined
            policy_object = g.policy_object

            autoassign_values = policy_object.\
                get_action_values(action=ACTION.AUTOASSIGN,
                                  scope=SCOPE.ENROLL,
                                  user=user_obj.login,
                                  resolver=user_obj.resolver,
                                  realm=user_obj.realm,
                                  client=g.client_ip)

            if len(autoassign_values) > 1:
                raise PolicyError("Contradicting Autoassign policies.")
            # check if the user has no token
            if autoassign_values and get_tokens(user=user_obj,
                                                count=True) == 0:
                # Check is the token would match
                # get all unassigned tokens in the realm and look for
                # a matching OTP:
                realm_tokens = get_tokens(realm=user_obj.realm, assigned=False)

                for token_obj in realm_tokens:
                    (res, pin, otp) = token_obj.split_pin_pass(password)
                    if res:
                        pin_check = True
                        if autoassign_values[0] == \
                                AUTOASSIGNVALUE.USERSTORE:
                            # If the autoassign policy is set to userstore,
                            # we need to check against the userstore.
                            pin_check = user_obj.check_password(pin)
                        if pin_check:
                            otp_check = token_obj.check_otp(otp)
                            if otp_check >= 0:
                                # we found a matching token
                                #    check MAXTOKENUSER and MAXTOKENREALM
                                check_max_token_user(request=request)
                                check_max_token_realm(request=request)
                                #    Assign token
                                assign_token(serial=token_obj.token.serial,
                                             user=user_obj,
                                             pin=pin)
                                # Set the response to true
                                content.get("result")["value"] = True
                                # Set the serial number
                                if not content.get("detail"):
                                    content["detail"] = {}
                                content.get("detail")["serial"] = \
                                    token_obj.token.serial
                                content.get("detail")["otplen"] = \
                                    token_obj.token.otplen
                                content.get("detail")["type"] = token_obj.type
                                content.get("detail")["message"] = "Token " \
                                                                   "assigned to " \
                                                                   "user via " \
                                                                   "Autoassignment"
                                response.data = json.dumps(content)

                                g.audit_object.log({
                                    "success":
                                    True,
                                    "action_info":
                                    "Token assigned via auto assignment",
                                    "serial":
                                    token_obj.token.serial
                                })
                                break

    return response
Example #11
0
def check_pin_policy(pin, policy):
    """
    The policy to check a PIN can contain of "c", "n" and "s".
    "cn" means, that the PIN should contain a character and a number.
    "+cn" means, that the PIN should contain elements from the group of characters and numbers
    "-ns" means, that the PIN must not contain numbers or special characters
    "[12345]" means, that the PIN may only consist of the characters 1,2,3,4 and 5.

    :param pin: The PIN to check
    :param policy: The policy that describes the allowed contents of the PIN.
    :return: Tuple of True or False and a description
    """
    chars = {
        "c": r"[a-zA-Z]",
        "n": r"[0-9]",
        "s": r"[\[\].:,;_<>+*!/()=?$§%&#~^-]"
    }
    ret = True
    comment = []

    if not policy:
        return False, "No policy given."

    if policy[0] in ["+", "-"] or policy[0] is not "[":
        for char in policy[1:]:
            if char not in chars.keys():
                raise PolicyError("Unknown character specifier in PIN policy.")

    if policy[0] == "+":
        # grouping
        necessary = []
        for char in policy[1:]:
            necessary.append(chars.get(char))
        necessary = "|".join(necessary)
        if not re.search(necessary, pin):
            ret = False
            comment.append("Missing character in PIN: {0!s}".format(necessary))

    elif policy[0] == "-":
        # exclusion
        not_allowed = []
        for char in policy[1:]:
            not_allowed.append(chars.get(char))
        not_allowed = "|".join(not_allowed)
        if re.search(not_allowed, pin):
            ret = False
            comment.append("Not allowed character in PIN!")

    elif policy[0] == "[" and policy[-1] == "]":
        # only allowed characters
        allowed_chars = policy[1:-1]
        for ch in pin:
            if ch not in allowed_chars:
                ret = False
                comment.append("Not allowed character in PIN!")
    else:
        for c in chars:
            if c in policy and not re.search(chars[c], pin):
                ret = False
                comment.append("Missing character in PIN: {0!s}".format(
                    chars[c]))

    return ret, ",".join(comment)
Example #12
0
    def _api_endpoint_get(cls, g, request_data):
        """ Handle all GET requests to the api endpoint.

        Currently this is only used for polling.
        :param g: The Flask context
        :param request_data: Dictionary containing the parameters of the request
        :type request_data: dict
        :returns: Result of the polling operation, 'True' if an unanswered and
                  matching challenge exists, 'False' otherwise.
        :rtype: bool
        """
        # By default we allow polling if the policy is not set.
        allow_polling = get_action_values_from_options(
            SCOPE.AUTH, PUSH_ACTION.ALLOW_POLLING,
            options={'g': g}) or PushAllowPolling.ALLOW
        if allow_polling == PushAllowPolling.DENY:
            raise PolicyError('Polling not allowed!')
        serial = getParam(request_data, "serial", optional=False)
        timestamp = getParam(request_data, 'timestamp', optional=False)
        signature = getParam(request_data, 'signature', optional=False)
        # first check if the timestamp is in the required span
        cls._check_timestamp_in_range(timestamp, POLL_TIME_WINDOW)
        # now check the signature
        # first get the token
        try:
            tok = get_one_token(serial=serial, tokentype=cls.get_class_type())
            # If the push_allow_polling policy is set to "token" we also
            # need to check the POLLING_ALLOWED tokeninfo. If it evaluated
            # to 'False', polling is not allowed for this token. If the
            # tokeninfo value evaluates to 'True' or is not set at all,
            # polling is allowed for this token.
            if allow_polling == PushAllowPolling.TOKEN:
                if not is_true(tok.get_tokeninfo(POLLING_ALLOWED, default='True')):
                    log.debug('Polling not allowed for pushtoken {0!s} due to '
                              'tokeninfo.'.format(serial))
                    raise PolicyError('Polling not allowed!')

            pubkey_obj = _build_verify_object(tok.get_tokeninfo(PUBLIC_KEY_SMARTPHONE))
            sign_data = u"{serial}|{timestamp}".format(**request_data)
            pubkey_obj.verify(b32decode(signature),
                              sign_data.encode("utf8"),
                              padding.PKCS1v15(),
                              hashes.SHA256())
            # The signature was valid now check for an open challenge
            # we need the private server key to sign the smartphone data
            pem_privkey = tok.get_tokeninfo(PRIVATE_KEY_SERVER)
            # we also need the FirebaseGateway for this token
            fb_identifier = tok.get_tokeninfo(PUSH_ACTION.FIREBASE_CONFIG)
            if not fb_identifier:
                raise ResourceNotFoundError('The pushtoken {0!s} has no Firebase configuration '
                                            'assigned.'.format(serial))
            fb_gateway = create_sms_instance(fb_identifier)
            options = {'g': g}
            challenges = []
            challengeobject_list = get_challenges(serial=serial)
            for chal in challengeobject_list:
                # check if the challenge is active and not already answered
                _cnt, answered = chal.get_otp_status()
                if not answered and chal.is_valid():
                    # then return the necessary smartphone data to answer
                    # the challenge
                    sp_data = _build_smartphone_data(serial, chal.challenge,
                                                     fb_gateway, pem_privkey, options)
                    challenges.append(sp_data)
            # return the challenges as a list in the result value
            result = challenges
        except (ResourceNotFoundError, ParameterError,
                InvalidSignature, ConfigAdminError, BinasciiError) as e:
            # to avoid disclosing information we always fail with an invalid
            # signature error even if the token with the serial could not be found
            log.debug('{0!s}'.format(traceback.format_exc()))
            log.info('The following error occurred during the signature '
                     'check: "{0!r}"'.format(e))
            raise privacyIDEAError('Could not verify signature!')

        return result
Example #13
0
    def check_otp(self, otpval, counter=None, window=None, options=None):
        """
        This checks the response of a previous challenge.
        :param otpval: N/A
        :param counter: The authentication counter
        :param window: N/A
        :param options: contains "clientdata", "signaturedata" and
            "transaction_id"
        :return: A value > 0 in case of success
        """
        ret = -1
        clientdata = options.get("clientdata")
        signaturedata = options.get("signaturedata")
        transaction_id = options.get("transaction_id")
        # The challenge in the challenge DB object is saved in hex
        challenge = binascii.unhexlify(options.get("challenge", ""))
        if clientdata and signaturedata and transaction_id and challenge:
            # This is a valid response for a U2F token
            challenge_url = url_encode(challenge)
            clientdata = url_decode(clientdata)
            clientdata_dict = json.loads(to_unicode(clientdata))
            client_challenge = clientdata_dict.get("challenge")
            if challenge_url != client_challenge:
                return ret
            if clientdata_dict.get("typ") != "navigator.id.getAssertion":
                raise ValidateError("Incorrect navigator.id")
            #client_origin = clientdata_dict.get("origin")
            signaturedata = url_decode(signaturedata)
            signaturedata_hex = hexlify_and_unicode(signaturedata)
            user_presence, counter, signature = parse_response_data(
                signaturedata_hex)

            user_pub_key = self.get_tokeninfo("pubKey")
            app_id = self.get_tokeninfo("appId")
            if check_response(user_pub_key, app_id, clientdata,
                              hexlify_and_unicode(signature), counter,
                              user_presence):
                # Signature verified.
                # check, if the counter increased!
                if counter > self.get_otp_count():
                    self.set_otp_count(counter)
                    ret = counter
                    # At this point we can check, if the attestation
                    # certificate is authorized.
                    # If not, we can raise a policy exception
                    if not attestation_certificate_allowed(
                        {
                            "attestation_issuer": self.get_tokeninfo("attestation_issuer"),
                            "attestation_serial": self.get_tokeninfo("attestation_serial"),
                            "attestation_subject": self.get_tokeninfo("attestation_subject")
                        },
                        Match
                            .user(options.get("g"),
                                  scope=SCOPE.AUTHZ,
                                  action=U2FACTION.REQ,
                                  user_object=self.user if self.user else None)
                            .action_values(unique=False)
                    ):
                        log.warning(
                            "The U2F device {0!s} is not allowed to authenticate due to policy restriction"
                                .format(self.token.serial))
                        raise PolicyError("The U2F device is not allowed "
                                          "to authenticate due to policy "
                                          "restriction.")

                else:
                    log.warning("The signature of %s was valid, but contained "
                                "an old counter." % self.token.serial)
            else:
                log.warning("Checking response for token {0!s} failed.".format(
                            self.token.serial))

        return ret