Exemplo n.º 1
0
    def get_trusted_domain_object_sid(self, object_name):
        result = pysss_nss_idmap.getsidbyname(object_name)
        if object_name in result and (pysss_nss_idmap.SID_KEY in result[object_name]):
            object_sid = result[object_name][pysss_nss_idmap.SID_KEY]
            return object_sid

        # Else, we are going to contact AD DC LDAP
        components = normalize_name(object_name)
        if not ('domain' in components or 'flatname' in components):
            # No domain or realm specified, ambiguous search
             raise errors.ValidationError(name=_('trusted domain object'),
                   error= _('Ambiguous search, user domain was not specified'))

        attrs = ['objectSid']
        filter = '(&(sAMAccountName=%(name)s)(|(objectClass=user)(objectClass=group)))' \
                % dict(name=components['name'])
        scope = _ldap.SCOPE_SUBTREE
        entries = self.get_trusted_domain_objects(components.get('domain'),
                components.get('flatname'), filter, attrs, scope)

        if len(entries) > 1:
            # Treat non-unique entries as invalid
            raise errors.ValidationError(name=_('trusted domain object'),
               error= _('Trusted domain did not return a unique object'))
        sid = self.__sid_to_str(entries[0][1]['objectSid'][0])
        try:
            test_sid = security.dom_sid(sid)
            return unicode(test_sid)
        except TypeError, e:
            raise errors.ValidationError(name=_('trusted domain object'),
               error= _('Trusted domain did not return a valid SID for the object'))
Exemplo n.º 2
0
    def get_sid_trusted_domain_object(self, object_name):
        """Returns SID for the trusted domain object (user or group only)"""
        if not self.domain:
            # our domain is not configured or self.is_configured() never run
            return None
        if not self._domains:
            self._domains = self.get_trusted_domains()
        if len(self._domains) == 0:
            # Our domain is configured but no trusted domains are configured
            return None

        components = normalize_name(object_name)
        if not ('domain' in components or 'flatname' in components):
            # No domain or realm specified, ambiguous search
            return False

        entry = None
        if 'domain' in components and components['domain'] in self._domains:
            # Now we have a name to check against our list of trusted domains
            entry = self.resolve_against_gc(components['domain'],
                                            components['name'])
        elif 'flatname' in components:
            # Flatname was specified, traverse through the list of trusted
            # domains first to find the proper one
            for domain in self._domains:
                if self._domains[domain][0] == components['flatname']:
                    entry = self.resolve_against_gc(domain, components['name'])
                    if entry:
                        break
        if entry:
            try:
                test_sid = security.dom_sid(entry)
                return unicode(test_sid)
            except TypeError, e:
                return False
Exemplo n.º 3
0
    def get_sid_trusted_domain_object(self, object_name):
        """Returns SID for the trusted domain object (user or group only)"""
        if not self.domain:
            # our domain is not configured or self.is_configured() never run
            return None
        if not self._domains:
            self._domains = self.get_trusted_domains()
        if len(self._domains) == 0:
            # Our domain is configured but no trusted domains are configured
            return None

        components = normalize_name(object_name)
        if not ('domain' in components or 'flatname' in components):
            # No domain or realm specified, ambiguous search
            return False

        entry = None
        if 'domain' in components and components['domain'] in self._domains:
            # Now we have a name to check against our list of trusted domains
            entry = self.resolve_against_gc(components['domain'], components['name'])
        elif 'flatname' in components:
            # Flatname was specified, traverse through the list of trusted
            # domains first to find the proper one
            for domain in self._domains:
                if self._domains[domain][0] == components['flatname']:
                    entry = self.resolve_against_gc(domain, components['name'])
                    if entry:
                        break
        if entry:
            try:
                test_sid = security.dom_sid(entry)
                return unicode(test_sid)
            except TypeError, e:
                return False
Exemplo n.º 4
0
    def __get_trusted_domain_user_and_groups(self, object_name):
        """
        Returns a tuple with user SID and a list of SIDs of all groups he is
        a member of.

        LIMITATIONS:
            - only Trusted Admins group members can use this function as it
              uses secret for IPA-Trusted domain link
            - List of group SIDs does not contain group memberships outside
              of the trusted domain
        """
        components = normalize_name(object_name)
        domain = components.get('domain')
        flatname = components.get('flatname')
        name = components.get('name')

        is_valid_sid = is_sid_valid(object_name)
        if is_valid_sid:
            # Find a trusted domain for the SID
            domain = self.get_domain_by_sid(object_name)
            # Now search a trusted domain for a user with this SID
            attrs = ['cn']
            filter = '(&(objectClass=user)(objectSid=%(sid)s))' \
                    % dict(sid=object_name)
            try:
                entries = self.get_trusted_domain_objects(domain=domain, filter=filter,
                        attrs=attrs, scope=_ldap.SCOPE_SUBTREE)
            except errors.NotFound:
                raise errors.NotFound(reason=_('trusted domain user not found'))
            user_dn = entries[0][0]
        elif domain or flatname:
            attrs = ['cn']
            filter = '(&(sAMAccountName=%(name)s)(objectClass=user))' \
                    % dict(name=name)
            try:
                entries = self.get_trusted_domain_objects(domain,
                        flatname, filter, attrs, _ldap.SCOPE_SUBTREE)
            except errors.NotFound:
                raise errors.NotFound(reason=_('trusted domain user not found'))
            user_dn = entries[0][0]
        else:
            # No domain or realm specified, ambiguous search
            raise errors.ValidationError(name=_('trusted domain object'),
                   error= _('Ambiguous search, user domain was not specified'))

        # Get SIDs of user object and it's groups
        # tokenGroups attribute must be read with a scope BASE for a known user
        # distinguished name to avoid search error
        attrs = ['objectSID', 'tokenGroups']
        filter = "(objectClass=user)"
        entries = self.get_trusted_domain_objects(domain,
            flatname, filter, attrs, _ldap.SCOPE_BASE, user_dn)
        object_sid = self.__sid_to_str(entries[0][1]['objectSid'][0])
        group_sids = [self.__sid_to_str(sid) for sid in entries[0][1]['tokenGroups']]
        return (object_sid, group_sids)
Exemplo n.º 5
0
    def __call__(self, environ, start_response):
        self.debug('WSGI login_password.__call__:')

        # Get the user and password parameters from the request
        content_type = environ.get('CONTENT_TYPE', '').lower()
        if not content_type.startswith('application/x-www-form-urlencoded'):
            return self.bad_request(environ, start_response, "Content-Type must be application/x-www-form-urlencoded")

        method = environ.get('REQUEST_METHOD', '').upper()
        if method == 'POST':
            query_string = read_input(environ)
        else:
            return self.bad_request(environ, start_response, "HTTP request method must be POST")

        try:
            query_dict = urlparse.parse_qs(query_string)
        except Exception as e:
            return self.bad_request(environ, start_response, "cannot parse query data")

        user = query_dict.get('user', None)
        if user is not None:
            if len(user) == 1:
                user = user[0]
            else:
                return self.bad_request(environ, start_response, "more than one user parameter")
        else:
            return self.bad_request(environ, start_response, "no user specified")

        # allows login in the form user@SERVER_REALM or user@server_realm
        # FIXME: uppercasing may be removed when better handling of UPN
        #        is introduced

        parts = normalize_name(user)

        if "domain" in parts:
            # username is of the form user@SERVER_REALM or user@server_realm

            # check whether the realm is server's realm
            # Users from other realms are not supported
            # (they do not have necessary LDAP entry, LDAP connect will fail)

            if parts["domain"].upper()==self.api.env.realm:
                user=parts["name"]
            else:
                return self.unauthorized(environ, start_response, '', 'denied')

        elif "flatname" in parts:
            # username is of the form NetBIOS\user
            return self.unauthorized(environ, start_response, '', 'denied')

        else:
            # username is of the form user or of some wild form, e.g.
            # user@REALM1@REALM2 or NetBIOS1\NetBIOS2\user (see normalize_name)

            # wild form username will fail at kinit, so nothing needs to be done
            pass

        password = query_dict.get('password', None)
        if password is not None:
            if len(password) == 1:
                password = password[0]
            else:
                return self.bad_request(environ, start_response, "more than one password parameter")
        else:
            return self.bad_request(environ, start_response, "no password specified")

        # Get the ccache we'll use and attempt to get credentials in it with user,password
        ipa_ccache_name = get_ipa_ccache_name()
        try:
            self.kinit(user, self.api.env.realm, password, ipa_ccache_name)
        except PasswordExpired as e:
            return self.unauthorized(environ, start_response, str(e), 'password-expired')
        except InvalidSessionPassword as e:
            return self.unauthorized(environ, start_response, str(e), 'invalid-password')

        return self.finalize_kerberos_acquisition('login_password', ipa_ccache_name, environ, start_response)
Exemplo n.º 6
0
    def execute(self, *args, **options):
        # First receive all needed information:
        # 1. HBAC rules (whether enabled or disabled)
        # 2. Required options are (user, target host, service)
        # 3. Options: rules to test (--rules, --enabled, --disabled), request for detail output
        rules = []

        # Use all enabled IPA rules by default
        all_enabled = True
        all_disabled = False

        # We need a local copy of test rules in order find incorrect ones
        testrules = {}
        if 'rules' in options:
            testrules = list(options['rules'])
            # When explicit rules are provided, disable assumptions
            all_enabled = False
            all_disabled = False

        sizelimit = None
        if 'sizelimit' in options:
            sizelimit = int(options['sizelimit'])

        # Check if --disabled is specified, include all disabled IPA rules
        if options['disabled']:
            all_disabled = True
            all_enabled = False

        # Finally, if enabled is specified implicitly, override above decisions
        if options['enabled']:
            all_enabled = True

        hbacset = []
        if len(testrules) == 0:
            hbacset = self.api.Command.hbacrule_find(
                sizelimit=sizelimit, no_members=False)['result']
        else:
            for rule in testrules:
                try:
                    hbacset.append(self.api.Command.hbacrule_show(rule)['result'])
                except Exception:
                    pass

        # We have some rules, import them
        # --enabled will import all enabled rules (default)
        # --disabled will import all disabled rules
        # --rules will implicitly add the rules from a rule list
        for rule in hbacset:
            ipa_rule = _convert_to_ipa_rule(rule)
            if ipa_rule.name in testrules:
                ipa_rule.enabled = True
                rules.append(ipa_rule)
                testrules.remove(ipa_rule.name)
            elif all_enabled and ipa_rule.enabled:
                # Option --enabled forces to include all enabled IPA rules into test
                rules.append(ipa_rule)
            elif all_disabled and not ipa_rule.enabled:
                # Option --disabled forces to include all disabled IPA rules into test
                ipa_rule.enabled = True
                rules.append(ipa_rule)

        # Check if there are unresolved rules left
        if len(testrules) > 0:
            # Error, unresolved rules are left in --rules
            return {'summary' : unicode(_(u'Unresolved rules in --rules')),
                    'error': testrules, 'matched': None, 'notmatched': None,
                    'warning' : None, 'value' : False}

        # Rules are converted to pyhbac format, build request and then test it
        request = pyhbac.HbacRequest()

        if options['user'] != u'all':
            # check first if this is not a trusted domain user
            if _dcerpc_bindings_installed:
                is_valid_sid = ipaserver.dcerpc.is_sid_valid(options['user'])
            else:
                is_valid_sid = False
            components = util.normalize_name(options['user'])
            if is_valid_sid or 'domain' in components or 'flatname' in components:
                # this is a trusted domain user
                if not _dcerpc_bindings_installed:
                    raise errors.NotFound(reason=_(
                        'Cannot perform external member validation without '
                        'Samba 4 support installed. Make sure you have installed '
                        'server-trust-ad sub-package of IPA on the server'))
                domain_validator = ipaserver.dcerpc.DomainValidator(self.api)
                if not domain_validator.is_configured():
                    raise errors.NotFound(reason=_(
                        'Cannot search in trusted domains without own domain configured. '
                        'Make sure you have run ipa-adtrust-install on the IPA server first'))
                user_sid, group_sids = domain_validator.get_trusted_domain_user_and_groups(options['user'])
                request.user.name = user_sid

                # Now search for all external groups that have this user or
                # any of its groups in its external members. Found entires
                # memberOf links will be then used to gather all groups where
                # this group is assigned, including the nested ones
                filter_sids = "(&(objectclass=ipaexternalgroup)(|(ipaExternalMember=%s)))" \
                        % ")(ipaExternalMember=".join(group_sids + [user_sid])

                ldap = self.api.Backend.ldap2
                group_container = DN(api.env.container_group, api.env.basedn)
                try:
                    entries, _truncated = ldap.find_entries(
                        filter_sids, ['memberof'], group_container)
                except errors.NotFound:
                    request.user.groups = []
                else:
                    groups = []
                    for entry in entries:
                        memberof_dns = entry.get('memberof', [])
                        for memberof_dn in memberof_dns:
                            if memberof_dn.endswith(group_container):
                                groups.append(memberof_dn[0][0].value)
                    request.user.groups = sorted(set(groups))
            else:
                # try searching for a local user
                try:
                    request.user.name = options['user']
                    search_result = self.api.Command.user_show(request.user.name)['result']
                    groups = search_result['memberof_group']
                    if 'memberofindirect_group' in search_result:
                        groups += search_result['memberofindirect_group']
                    request.user.groups = sorted(set(groups))
                except Exception:
                    pass

        if options['service'] != u'all':
            try:
                request.service.name = options['service']
                service_result = self.api.Command.hbacsvc_show(request.service.name)['result']
                if 'memberof_hbacsvcgroup' in service_result:
                    request.service.groups = service_result['memberof_hbacsvcgroup']
            except Exception:
                pass

        if options['targethost'] != u'all':
            try:
                request.targethost.name = self.canonicalize(options['targethost'])
                tgthost_result = self.api.Command.host_show(request.targethost.name)['result']
                groups = tgthost_result['memberof_hostgroup']
                if 'memberofindirect_hostgroup' in tgthost_result:
                    groups += tgthost_result['memberofindirect_hostgroup']
                request.targethost.groups = sorted(set(groups))
            except Exception:
                pass

        matched_rules = []
        notmatched_rules = []
        error_rules = []
        warning_rules = []

        result = {'warning':None, 'matched':None, 'notmatched':None, 'error':None}
        if not options['nodetail']:
            # Validate runs rules one-by-one and reports failed ones
            for ipa_rule in rules:
                try:
                    res = request.evaluate([ipa_rule])
                    if res == pyhbac.HBAC_EVAL_ALLOW:
                        matched_rules.append(ipa_rule.name)
                    if res == pyhbac.HBAC_EVAL_DENY:
                        notmatched_rules.append(ipa_rule.name)
                except pyhbac.HbacError as e:
                    code, rule_name = e.args
                    if code == pyhbac.HBAC_EVAL_ERROR:
                        error_rules.append(rule_name)
                        self.log.info('Native IPA HBAC rule "%s" parsing error: %s' % \
                                      (rule_name, pyhbac.hbac_result_string(code)))
                except (TypeError, IOError) as info:
                    self.log.error('Native IPA HBAC module error: %s' % info)

            access_granted = len(matched_rules) > 0
        else:
            res = request.evaluate(rules)
            access_granted = (res == pyhbac.HBAC_EVAL_ALLOW)

        result['summary'] = _('Access granted: %s') % (access_granted)


        if len(matched_rules) > 0:
            result['matched'] = matched_rules
        if len(notmatched_rules) > 0:
            result['notmatched'] = notmatched_rules
        if len(error_rules) > 0:
            result['error'] = error_rules
        if len(warning_rules) > 0:
            result['warning'] = warning_rules

        result['value'] = access_granted
        return result
Exemplo n.º 7
0
    def __call__(self, environ, start_response):
        self.debug('WSGI login_password.__call__:')

        # Get the user and password parameters from the request
        content_type = environ.get('CONTENT_TYPE', '').lower()
        if not content_type.startswith('application/x-www-form-urlencoded'):
            return self.bad_request(
                environ, start_response,
                "Content-Type must be application/x-www-form-urlencoded")

        method = environ.get('REQUEST_METHOD', '').upper()
        if method == 'POST':
            query_string = read_input(environ)
        else:
            return self.bad_request(environ, start_response,
                                    "HTTP request method must be POST")

        try:
            query_dict = parse_qs(query_string)
        except Exception as e:
            return self.bad_request(environ, start_response,
                                    "cannot parse query data")

        user = query_dict.get('user', None)
        if user is not None:
            if len(user) == 1:
                user = user[0]
            else:
                return self.bad_request(environ, start_response,
                                        "more than one user parameter")
        else:
            return self.bad_request(environ, start_response,
                                    "no user specified")

        # allows login in the form user@SERVER_REALM or user@server_realm
        # FIXME: uppercasing may be removed when better handling of UPN
        #        is introduced

        parts = normalize_name(user)

        if "domain" in parts:
            # username is of the form user@SERVER_REALM or user@server_realm

            # check whether the realm is server's realm
            # Users from other realms are not supported
            # (they do not have necessary LDAP entry, LDAP connect will fail)

            if parts["domain"].upper() == self.api.env.realm:
                user = parts["name"]
            else:
                return self.unauthorized(environ, start_response, '', 'denied')

        elif "flatname" in parts:
            # username is of the form NetBIOS\user
            return self.unauthorized(environ, start_response, '', 'denied')

        else:
            # username is of the form user or of some wild form, e.g.
            # user@REALM1@REALM2 or NetBIOS1\NetBIOS2\user (see normalize_name)

            # wild form username will fail at kinit, so nothing needs to be done
            pass

        password = query_dict.get('password', None)
        if password is not None:
            if len(password) == 1:
                password = password[0]
            else:
                return self.bad_request(environ, start_response,
                                        "more than one password parameter")
        else:
            return self.bad_request(environ, start_response,
                                    "no password specified")

        # Get the ccache we'll use and attempt to get credentials in it with user,password
        ipa_ccache_name = get_ipa_ccache_name()
        try:
            self.kinit(user, self.api.env.realm, password, ipa_ccache_name)
        except PasswordExpired as e:
            return self.unauthorized(environ, start_response, str(e),
                                     'password-expired')
        except InvalidSessionPassword as e:
            return self.unauthorized(environ, start_response, str(e),
                                     'invalid-password')
        except KrbPrincipalExpired as e:
            return self.unauthorized(environ, start_response, str(e),
                                     'krbprincipal-expired')
        except UserLocked as e:
            return self.unauthorized(environ, start_response, str(e),
                                     'user-locked')

        return self.finalize_kerberos_acquisition('login_password',
                                                  ipa_ccache_name, environ,
                                                  start_response)
Exemplo n.º 8
0
    def execute(self, *args, **options):
        # First receive all needed information:
        # 1. HBAC rules (whether enabled or disabled)
        # 2. Required options are (user, target host, service)
        # 3. Options: rules to test (--rules, --enabled, --disabled), request for detail output
        rules = []

        # Use all enabled IPA rules by default
        all_enabled = True
        all_disabled = False

        # We need a local copy of test rules in order find incorrect ones
        testrules = {}
        if 'rules' in options:
            testrules = list(options['rules'])
            # When explicit rules are provided, disable assumptions
            all_enabled = False
            all_disabled = False

        sizelimit = None
        if 'sizelimit' in options:
            sizelimit = int(options['sizelimit'])

        # Check if --disabled is specified, include all disabled IPA rules
        if options['disabled']:
            all_disabled = True
            all_enabled = False

        # Finally, if enabled is specified implicitly, override above decisions
        if options['enabled']:
            all_enabled = True

        hbacset = []
        if len(testrules) == 0:
            hbacset = self.api.Command.hbacrule_find(
                sizelimit=sizelimit, no_members=False)['result']
        else:
            for rule in testrules:
                try:
                    hbacset.append(self.api.Command.hbacrule_show(rule)['result'])
                except Exception:
                    pass

        # We have some rules, import them
        # --enabled will import all enabled rules (default)
        # --disabled will import all disabled rules
        # --rules will implicitly add the rules from a rule list
        for rule in hbacset:
            ipa_rule = convert_to_ipa_rule(rule)
            if ipa_rule.name in testrules:
                ipa_rule.enabled = True
                rules.append(ipa_rule)
                testrules.remove(ipa_rule.name)
            elif all_enabled and ipa_rule.enabled:
                # Option --enabled forces to include all enabled IPA rules into test
                rules.append(ipa_rule)
            elif all_disabled and not ipa_rule.enabled:
                # Option --disabled forces to include all disabled IPA rules into test
                ipa_rule.enabled = True
                rules.append(ipa_rule)

        # Check if there are unresolved rules left
        if len(testrules) > 0:
            # Error, unresolved rules are left in --rules
            return {'summary' : unicode(_(u'Unresolved rules in --rules')),
                    'error': testrules, 'matched': None, 'notmatched': None,
                    'warning' : None, 'value' : False}

        # Rules are converted to pyhbac format, build request and then test it
        request = pyhbac.HbacRequest()

        if options['user'] != u'all':
            # check first if this is not a trusted domain user
            if _dcerpc_bindings_installed:
                is_valid_sid = ipaserver.dcerpc.is_sid_valid(options['user'])
            else:
                is_valid_sid = False
            components = util.normalize_name(options['user'])
            if is_valid_sid or 'domain' in components or 'flatname' in components:
                # this is a trusted domain user
                if not _dcerpc_bindings_installed:
                    raise errors.NotFound(reason=_(
                        'Cannot perform external member validation without '
                        'Samba 4 support installed. Make sure you have installed '
                        'server-trust-ad sub-package of IPA on the server'))
                domain_validator = ipaserver.dcerpc.DomainValidator(self.api)
                if not domain_validator.is_configured():
                    raise errors.NotFound(reason=_(
                        'Cannot search in trusted domains without own domain configured. '
                        'Make sure you have run ipa-adtrust-install on the IPA server first'))
                user_sid, group_sids = domain_validator.get_trusted_domain_user_and_groups(options['user'])
                request.user.name = user_sid

                # Now search for all external groups that have this user or
                # any of its groups in its external members. Found entires
                # memberOf links will be then used to gather all groups where
                # this group is assigned, including the nested ones
                filter_sids = "(&(objectclass=ipaexternalgroup)(|(ipaExternalMember=%s)))" \
                        % ")(ipaExternalMember=".join(group_sids + [user_sid])

                ldap = self.api.Backend.ldap2
                group_container = DN(api.env.container_group, api.env.basedn)
                try:
                    entries, _truncated = ldap.find_entries(
                        filter_sids, ['memberof'], group_container)
                except errors.NotFound:
                    request.user.groups = []
                else:
                    groups = []
                    for entry in entries:
                        memberof_dns = entry.get('memberof', [])
                        for memberof_dn in memberof_dns:
                            if memberof_dn.endswith(group_container):
                                groups.append(memberof_dn[0][0].value)
                    request.user.groups = sorted(set(groups))
            else:
                # try searching for a local user
                try:
                    request.user.name = options['user']
                    search_result = self.api.Command.user_show(request.user.name)['result']
                    groups = search_result['memberof_group']
                    if 'memberofindirect_group' in search_result:
                        groups += search_result['memberofindirect_group']
                    request.user.groups = sorted(set(groups))
                except Exception:
                    pass

        if options['service'] != u'all':
            try:
                request.service.name = options['service']
                service_result = self.api.Command.hbacsvc_show(request.service.name)['result']
                if 'memberof_hbacsvcgroup' in service_result:
                    request.service.groups = service_result['memberof_hbacsvcgroup']
            except Exception:
                pass

        if options['targethost'] != u'all':
            try:
                request.targethost.name = self.canonicalize(options['targethost'])
                tgthost_result = self.api.Command.host_show(request.targethost.name)['result']
                groups = tgthost_result['memberof_hostgroup']
                if 'memberofindirect_hostgroup' in tgthost_result:
                    groups += tgthost_result['memberofindirect_hostgroup']
                request.targethost.groups = sorted(set(groups))
            except Exception:
                pass

        matched_rules = []
        notmatched_rules = []
        error_rules = []
        warning_rules = []

        result = {'warning':None, 'matched':None, 'notmatched':None, 'error':None}
        if not options['nodetail']:
            # Validate runs rules one-by-one and reports failed ones
            for ipa_rule in rules:
                try:
                    res = request.evaluate([ipa_rule])
                    if res == pyhbac.HBAC_EVAL_ALLOW:
                        matched_rules.append(ipa_rule.name)
                    if res == pyhbac.HBAC_EVAL_DENY:
                        notmatched_rules.append(ipa_rule.name)
                except pyhbac.HbacError as e:
                    code, rule_name = e.args
                    if code == pyhbac.HBAC_EVAL_ERROR:
                        error_rules.append(rule_name)
                        self.log.info('Native IPA HBAC rule "%s" parsing error: %s' % \
                                      (rule_name, pyhbac.hbac_result_string(code)))
                except (TypeError, IOError) as info:
                    self.log.error('Native IPA HBAC module error: %s' % info)

            access_granted = len(matched_rules) > 0
        else:
            res = request.evaluate(rules)
            access_granted = (res == pyhbac.HBAC_EVAL_ALLOW)

        result['summary'] = _('Access granted: %s') % (access_granted)


        if len(matched_rules) > 0:
            result['matched'] = matched_rules
        if len(notmatched_rules) > 0:
            result['notmatched'] = notmatched_rules
        if len(error_rules) > 0:
            result['error'] = error_rules
        if len(warning_rules) > 0:
            result['warning'] = warning_rules

        result['value'] = access_granted
        return result
Exemplo n.º 9
0
class login_password(Backend, KerberosSession, HTTP_Status):

    content_type = 'text/plain'
    key = '/session/login_password'

    def __init__(self):
        super(login_password, self).__init__()

    def _on_finalize(self):
        super(login_password, self)._on_finalize()
        self.api.Backend.wsgi_dispatch.mount(self, self.key)
        self.kerb_session_on_finalize()

    def __call__(self, environ, start_response):
        self.debug('WSGI login_password.__call__:')

        # Get the user and password parameters from the request
        content_type = environ.get('CONTENT_TYPE', '').lower()
        if not content_type.startswith('application/x-www-form-urlencoded'):
            return self.bad_request(
                environ, start_response,
                "Content-Type must be application/x-www-form-urlencoded")

        method = environ.get('REQUEST_METHOD', '').upper()
        if method == 'POST':
            query_string = read_input(environ)
        else:
            return self.bad_request(environ, start_response,
                                    "HTTP request method must be POST")

        try:
            query_dict = urlparse.parse_qs(query_string)
        except Exception, e:
            return self.bad_request(environ, start_response,
                                    "cannot parse query data")

        user = query_dict.get('user', None)
        if user is not None:
            if len(user) == 1:
                user = user[0]
            else:
                return self.bad_request(environ, start_response,
                                        "more than one user parameter")
        else:
            return self.bad_request(environ, start_response,
                                    "no user specified")

        # allows login in the form user@SERVER_REALM or user@server_realm
        # FIXME: uppercasing may be removed when better handling of UPN
        #        is introduced

        parts = normalize_name(user)

        if "domain" in parts:
            # username is of the form user@SERVER_REALM or user@server_realm

            # check whether the realm is server's realm
            # Users from other realms are not supported
            # (they do not have necessary LDAP entry, LDAP connect will fail)

            if parts["domain"].upper() == self.api.env.realm:
                user = parts["name"]
            else:
                return self.unauthorized(environ, start_response, '', 'denied')

        elif "flatname" in parts:
            # username is of the form NetBIOS\user
            return self.unauthorized(environ, start_response, '', 'denied')

        else:
            # username is of the form user or of some wild form, e.g.
            # user@REALM1@REALM2 or NetBIOS1\NetBIOS2\user (see normalize_name)

            # wild form username will fail at kinit, so nothing needs to be done
            pass

        password = query_dict.get('password', None)
        if password is not None:
            if len(password) == 1:
                password = password[0]
            else:
                return self.bad_request(environ, start_response,
                                        "more than one password parameter")
        else:
            return self.bad_request(environ, start_response,
                                    "no password specified")

        # Get the ccache we'll use and attempt to get credentials in it with user,password
        ipa_ccache_name = get_ipa_ccache_name()
        reason = 'invalid-password'
        try:
            self.kinit(user, self.api.env.realm, password, ipa_ccache_name)
        except InvalidSessionPassword, e:
            # Ok, now why is this bad. Is the password simply bad or is the
            # password expired?
            try:
                dn = DN(('uid', user), self.api.env.container_user,
                        self.api.env.basedn)
                conn = ldap2(shared_instance=False,
                             ldap_uri=self.api.env.ldap_uri)
                conn.connect(bind_dn=dn, bind_pw=password)

                # password is ok, must be expired, lets double-check
                (userdn,
                 entry_attrs) = conn.get_entry(dn, ['krbpasswordexpiration'])
                if 'krbpasswordexpiration' in entry_attrs:
                    expiration = entry_attrs['krbpasswordexpiration'][0]
                    try:
                        exp = time.strptime(expiration, '%Y%m%d%H%M%SZ')
                        if exp <= time.gmtime():
                            reason = 'password-expired'
                    except ValueError, v:
                        self.error('Unable to convert %s to a time string' %
                                   expiration)

            except Exception:
                # It doesn't really matter how we got here but the user's
                # password is not accepted or the user is unknown.
                pass
            finally:
                if conn.isconnected():
                    conn.destroy_connection()

            return self.unauthorized(environ, start_response, str(e), reason)