Пример #1
0
class hbacrule(LDAPObject):
    """
    HBAC object.
    """
    container_dn = api.env.container_hbac
    object_name = _('HBAC rule')
    object_name_plural = _('HBAC rules')
    object_class = ['ipaassociation', 'ipahbacrule']
    permission_filter_objectclasses = ['ipahbacrule']
    default_attributes = [
        'cn',
        'ipaenabledflag',
        'description',
        'usercategory',
        'hostcategory',
        'servicecategory',
        'ipaenabledflag',
        'memberuser',
        'sourcehost',
        'memberhost',
        'memberservice',
        'externalhost',
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_attribute = 'ipauniqueid'
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
        'sourcehost': ['host', 'hostgroup'],
        'memberservice': ['hbacsvc', 'hbacsvcgroup'],
    }
    managed_permissions = {
        'System: Read HBAC Rules': {
            'replaces_global_anonymous_aci': True,
            'ipapermbindruletype': 'all',
            'ipapermright': {'read', 'search', 'compare'},
            'ipapermdefaultattr': {
                'accessruletype',
                'accesstime',
                'cn',
                'description',
                'externalhost',
                'hostcategory',
                'ipaenabledflag',
                'ipauniqueid',
                'memberhost',
                'memberservice',
                'memberuser',
                'servicecategory',
                'sourcehost',
                'sourcehostcategory',
                'usercategory',
                'objectclass',
                'member',
            },
        },
        'System: Add HBAC Rule': {
            'ipapermright': {'add'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=hbac,$SUFFIX")(version 3.0;acl "permission:Add HBAC rule";allow (add) groupdn = "ldap:///cn=Add HBAC rule,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'HBAC Administrator'},
        },
        'System: Delete HBAC Rule': {
            'ipapermright': {'delete'},
            'replaces': [
                '(target = "ldap:///ipauniqueid=*,cn=hbac,$SUFFIX")(version 3.0;acl "permission:Delete HBAC rule";allow (delete) groupdn = "ldap:///cn=Delete HBAC rule,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'HBAC Administrator'},
        },
        'System: Manage HBAC Rule Membership': {
            'ipapermright': {'write'},
            'ipapermdefaultattr':
            {'externalhost', 'memberhost', 'memberservice', 'memberuser'},
            'replaces': [
                '(targetattr = "memberuser || externalhost || memberservice || memberhost")(target = "ldap:///ipauniqueid=*,cn=hbac,$SUFFIX")(version 3.0;acl "permission:Manage HBAC rule membership";allow (write) groupdn = "ldap:///cn=Manage HBAC rule membership,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'HBAC Administrator'},
        },
        'System: Modify HBAC Rule': {
            'ipapermright': {'write'},
            'ipapermdefaultattr': {
                'accessruletype', 'accesstime', 'cn', 'description',
                'hostcategory', 'ipaenabledflag', 'servicecategory',
                'sourcehost', 'sourcehostcategory', 'usercategory'
            },
            'replaces': [
                '(targetattr = "servicecategory || sourcehostcategory || cn || description || ipaenabledflag || accesstime || usercategory || hostcategory || accessruletype || sourcehost")(target = "ldap:///ipauniqueid=*,cn=hbac,$SUFFIX")(version 3.0;acl "permission:Modify HBAC rule";allow (write) groupdn = "ldap:///cn=Modify HBAC rule,cn=permissions,cn=pbac,$SUFFIX";)',
            ],
            'default_privileges': {'HBAC Administrator'},
        },
    }

    label = _('HBAC Rules')
    label_singular = _('HBAC Rule')

    takes_params = (
        Str(
            'cn',
            cli_name='name',
            label=_('Rule name'),
            primary_key=True,
        ),
        StrEnum(
            'accessruletype',
            validate_type,
            cli_name='type',
            doc=_('Rule type (allow)'),
            label=_('Rule type'),
            values=(u'allow', u'deny'),
            default=u'allow',
            autofill=True,
            exclude='webui',
            flags=['no_option', 'no_output'],
        ),
        # FIXME: {user,host,service}categories should expand in the future
        StrEnum(
            'usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the rule applies to'),
            values=(u'all', ),
        ),
        DeprecatedParam('sourcehostcategory?'),
        StrEnum(
            'servicecategory?',
            cli_name='servicecat',
            label=_('Service category'),
            doc=_('Service category the rule applies to'),
            values=(u'all', ),
        ),
        #        AccessTime('accesstime?',
        #            cli_name='time',
        #            label=_('Access time'),
        #        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool(
            'ipaenabledflag?',
            label=_('Enabled'),
            flags=['no_option'],
        ),
        Str(
            'memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        DeprecatedParam('sourcehost_host?'),
        DeprecatedParam('sourcehost_hostgroup?'),
        Str(
            'memberservice_hbacsvc?',
            label=_('Services'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberservice_hbacsvcgroup?',
            label=_('Service Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        external_host_param,
    )
Пример #2
0
class idrange_mod(LDAPUpdate):
    __doc__ = _("""Modify ID range.

{0}
""".format(ID_RANGE_VS_DNA_WARNING))

    msg_summary = _('Modified ID range "%(value)s"')

    takes_options = LDAPUpdate.takes_options + (
        DeprecatedParam('ipanttrusteddomainsid?'),
        DeprecatedParam('ipanttrusteddomainname?'),
    )

    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys,
                     **options):
        assert isinstance(dn, DN)
        attrs_list.append('objectclass')

        try:
            old_attrs = ldap.get_entry(dn, ['*'])
        except errors.NotFound:
            self.obj.handle_not_found(*keys)

        if old_attrs['iparangetype'][0] == 'ipa-local':
            raise errors.ExecutionError(
                message=_('This command can not be used to change ID '
                          'allocation for local IPA domain. Run '
                          '`ipa help idrange` for more information'))

        is_set = lambda x: (x in entry_attrs) and (entry_attrs[x] is not None)
        in_updated_attrs = lambda x:\
            (x in entry_attrs and entry_attrs[x] is not None) or\
            (x not in entry_attrs and x in old_attrs
                and old_attrs[x] is not None)

        # This needs to stay in options since there is no
        # ipanttrusteddomainname attribute in LDAP
        if 'ipanttrusteddomainname' in options:
            if is_set('ipanttrusteddomainsid'):
                raise errors.ValidationError(
                    name='ID Range setup',
                    error=_('Options dom-sid and dom-name '
                            'cannot be used together'))

            sid = self.obj.get_trusted_domain_sid_from_name(
                options['ipanttrusteddomainname'])

            # we translate the name into sid so further validation can rely
            # on ipanttrusteddomainsid attribute only
            if sid is not None:
                entry_attrs['ipanttrusteddomainsid'] = sid
            else:
                raise errors.ValidationError(
                    name='ID Range setup',
                    error=_('SID for the specified trusted domain name could '
                            'not be found. Please specify the SID directly '
                            'using dom-sid option.'))

        if in_updated_attrs('ipanttrusteddomainsid'):
            if in_updated_attrs('ipasecondarybaserid'):
                raise errors.ValidationError(
                    name='ID Range setup',
                    error=_('Options dom-sid and secondary-rid-base cannot '
                            'be used together'))
            range_type = old_attrs['iparangetype'][0]
            if range_type == u'ipa-ad-trust':
                if not in_updated_attrs('ipabaserid'):
                    raise errors.ValidationError(
                        name='ID Range setup',
                        error=_('Options dom-sid and rid-base must '
                                'be used together'))
            elif (range_type == u'ipa-ad-trust-posix'
                  and 'ipabaserid' in entry_attrs):
                if entry_attrs['ipabaserid'] is None:
                    entry_attrs['ipabaserid'] = 0
                elif entry_attrs['ipabaserid'] != 0:
                    raise errors.ValidationError(
                        name='ID Range setup',
                        error=_('Option rid-base must not be used when IPA '
                                'range type is ipa-ad-trust-posix'))

            if is_set('ipanttrusteddomainsid'):
                # Validate SID as the one of trusted domains
                # perform this check only if the attribute was changed
                self.obj.validate_trusted_domain_sid(
                    entry_attrs['ipanttrusteddomainsid'])

            # Add trusted AD domain range object class, if it wasn't there
            if not 'ipatrustedaddomainrange' in old_attrs['objectclass']:
                entry_attrs['objectclass'].append('ipatrustedaddomainrange')

        else:
            # secondary base rid must be set if and only if base rid is set
            if in_updated_attrs('ipasecondarybaserid') !=\
                in_updated_attrs('ipabaserid'):
                raise errors.ValidationError(
                    name='ID Range setup',
                    error=_('Options secondary-rid-base and rid-base must '
                            'be used together'))

        # ensure that primary and secondary rid ranges do not overlap
        if all(
                in_updated_attrs(base)
                for base in ('ipabaserid', 'ipasecondarybaserid')):

            # make sure we are working with updated attributes
            rid_range_attributes = ('ipabaserid', 'ipasecondarybaserid',
                                    'ipaidrangesize')
            updated_values = dict()

            for attr in rid_range_attributes:
                if is_set(attr):
                    updated_values[attr] = entry_attrs[attr]
                else:
                    updated_values[attr] = int(old_attrs[attr][0])

            if self.obj.are_rid_ranges_overlapping(
                    updated_values['ipabaserid'],
                    updated_values['ipasecondarybaserid'],
                    updated_values['ipaidrangesize']):
                raise errors.ValidationError(
                    name='ID Range setup',
                    error=_("Primary RID range and secondary RID range"
                            " cannot overlap"))

        # check whether ids are in modified range
        old_base_id = int(old_attrs.get('ipabaseid', [0])[0])
        old_range_size = int(old_attrs.get('ipaidrangesize', [0])[0])
        new_base_id = entry_attrs.get('ipabaseid')

        if new_base_id is not None:
            new_base_id = int(new_base_id)

        new_range_size = entry_attrs.get('ipaidrangesize')

        if new_range_size is not None:
            new_range_size = int(new_range_size)

        self.obj.check_ids_in_modified_range(old_base_id, old_range_size,
                                             new_base_id, new_range_size)

        return dn

    def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
        assert isinstance(dn, DN)
        self.obj.handle_ipabaserid(entry_attrs, options)
        self.obj.handle_iparangetype(entry_attrs, options)
        return dn
Пример #3
0
class hbacrule(LDAPObject):
    """
    HBAC object.
    """
    container_dn = api.env.container_hbac
    object_name = _('HBAC rule')
    object_name_plural = _('HBAC rules')
    object_class = ['ipaassociation', 'ipahbacrule']
    default_attributes = [
        'cn',
        'ipaenabledflag',
        'description',
        'usercategory',
        'hostcategory',
        'sourcehostcategory',
        'servicecategory',
        'ipaenabledflag',
        'memberuser',
        'sourcehost',
        'memberhost',
        'memberservice',
        'memberhostgroup',
        'externalhost',
    ]
    uuid_attribute = 'ipauniqueid'
    rdn_attribute = 'ipauniqueid'
    attribute_members = {
        'memberuser': ['user', 'group'],
        'memberhost': ['host', 'hostgroup'],
        'sourcehost': ['host', 'hostgroup'],
        'memberservice': ['hbacsvc', 'hbacsvcgroup'],
    }

    label = _('HBAC Rules')
    label_singular = _('HBAC Rule')

    takes_params = (
        Str(
            'cn',
            cli_name='name',
            label=_('Rule name'),
            primary_key=True,
        ),
        StrEnum(
            'accessruletype',
            validate_type,
            cli_name='type',
            doc=_('Rule type (allow)'),
            label=_('Rule type'),
            values=(u'allow', u'deny'),
            default=u'allow',
            autofill=True,
            exclude='webui',
            flags=['no_option', 'no_output'],
        ),
        # FIXME: {user,host,service}categories should expand in the future
        StrEnum(
            'usercategory?',
            cli_name='usercat',
            label=_('User category'),
            doc=_('User category the rule applies to'),
            values=(u'all', ),
        ),
        StrEnum(
            'hostcategory?',
            cli_name='hostcat',
            label=_('Host category'),
            doc=_('Host category the rule applies to'),
            values=(u'all', ),
        ),
        DeprecatedParam('sourcehostcategory?'),
        StrEnum(
            'servicecategory?',
            cli_name='servicecat',
            label=_('Service category'),
            doc=_('Service category the rule applies to'),
            values=(u'all', ),
        ),
        #        AccessTime('accesstime?',
        #            cli_name='time',
        #            label=_('Access time'),
        #        ),
        Str(
            'description?',
            cli_name='desc',
            label=_('Description'),
        ),
        Bool(
            'ipaenabledflag?',
            label=_('Enabled'),
            flags=['no_option'],
        ),
        Str(
            'memberuser_user?',
            label=_('Users'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberuser_group?',
            label=_('User Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_host?',
            label=_('Hosts'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberhost_hostgroup?',
            label=_('Host Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        DeprecatedParam('sourcehost_host?'),
        DeprecatedParam('sourcehost_hostgroup?'),
        Str(
            'memberservice_hbacsvc?',
            label=_('Services'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        Str(
            'memberservice_hbacsvcgroup?',
            label=_('Service Groups'),
            flags=['no_create', 'no_update', 'no_search'],
        ),
        external_host_param,
    )
Пример #4
0
class stageuser_add(baseuser_add):
    __doc__ = _('Add a new stage user.')

    msg_summary = _('Added stage user "%(value)s"')

    has_output_params = baseuser_add.has_output_params + stageuser_output_params

    takes_options = LDAPCreate.takes_options + (DeprecatedParam(
        'from_delete?',
        doc=_('Create Stage user in from a delete user'),
        cli_name='from_delete',
        default=False,
    ), )

    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys,
                     **options):
        assert isinstance(dn, DN)

        # then givenname and sn are required attributes
        if 'givenname' not in entry_attrs:
            raise errors.RequirementError(name='givenname',
                                          error=_('givenname is required'))

        if 'sn' not in entry_attrs:
            raise errors.RequirementError(name='sn', error=_('sn is required'))

        # we don't want an user private group to be created for this user
        # add NO_UPG_MAGIC description attribute to let the DS plugin know
        entry_attrs.setdefault('description', [])
        entry_attrs['description'].append(NO_UPG_MAGIC)

        # uidNumber/gidNumber
        entry_attrs.setdefault('uidnumber', baseldap.DNA_MAGIC)
        entry_attrs.setdefault('gidnumber', baseldap.DNA_MAGIC)

        if not client_has_capability(options['version'],
                                     'optional_uid_params'):
            # https://fedorahosted.org/freeipa/ticket/2886
            # Old clients say 999 (OLD_DNA_MAGIC) when they really mean
            # "assign a value dynamically".
            OLD_DNA_MAGIC = 999
            if entry_attrs.get('uidnumber') == OLD_DNA_MAGIC:
                entry_attrs['uidnumber'] = baseldap.DNA_MAGIC
            if entry_attrs.get('gidnumber') == OLD_DNA_MAGIC:
                entry_attrs['gidnumber'] = baseldap.DNA_MAGIC

        # Check the lenght of the RDN (uid) value
        config = ldap.get_ipa_config()
        if 'ipamaxusernamelength' in config:
            if len(keys[-1]) > int(config.get('ipamaxusernamelength')[0]):
                raise errors.ValidationError(
                    name=self.obj.primary_key.cli_name,
                    error=_('can be at most %(len)d characters') %
                    dict(len=int(config.get('ipamaxusernamelength')[0])))
        default_shell = config.get('ipadefaultloginshell', [paths.SH])[0]
        entry_attrs.setdefault('loginshell', default_shell)
        # hack so we can request separate first and last name in CLI
        full_name = '%s %s' % (entry_attrs['givenname'], entry_attrs['sn'])
        entry_attrs.setdefault('cn', full_name)

        # Homedirectory
        # (order is : option, placeholder (TBD), CLI default value (here in config))
        if 'homedirectory' not in entry_attrs:
            # get home's root directory from config
            homes_root = config.get('ipahomesrootdir', [paths.HOME_DIR])[0]
            # build user's home directory based on his uid
            entry_attrs['homedirectory'] = posixpath.join(homes_root, keys[-1])

        # Kerberos principal
        entry_attrs.setdefault('krbprincipalname',
                               '%s@%s' % (entry_attrs['uid'], api.env.realm))

        # If requested, generate a userpassword
        if 'userpassword' not in entry_attrs and options.get('random'):
            entry_attrs['userpassword'] = ipa_generate_password(
                baseuser_pwdchars)
            # save the password so it can be displayed in post_callback
            setattr(context, 'randompassword', entry_attrs['userpassword'])

        # Check the email or create it
        if 'mail' in entry_attrs:
            entry_attrs['mail'] = self.obj.normalize_and_validate_email(
                entry_attrs['mail'], config)
        else:
            # No e-mail passed in. If we have a default e-mail domain set
            # then we'll add it automatically.
            defaultdomain = config.get('ipadefaultemaildomain', [None])[0]
            if defaultdomain:
                entry_attrs['mail'] = self.obj.normalize_and_validate_email(
                    keys[-1], config)

        # If the manager is defined, check it is a ACTIVE user to validate it
        if 'manager' in entry_attrs:
            entry_attrs['manager'] = self.obj.normalize_manager(
                entry_attrs['manager'], self.obj.active_container_dn)

        if ('objectclass' in entry_attrs and 'userclass' in entry_attrs
                and 'ipauser' not in entry_attrs['objectclass']):
            entry_attrs['objectclass'].append('ipauser')

        if 'ipatokenradiusconfiglink' in entry_attrs:
            cl = entry_attrs['ipatokenradiusconfiglink']
            if cl:
                if 'objectclass' not in entry_attrs:
                    _entry = ldap.get_entry(dn, ['objectclass'])
                    entry_attrs['objectclass'] = _entry['objectclass']

                if 'ipatokenradiusproxyuser' not in entry_attrs['objectclass']:
                    entry_attrs['objectclass'].append(
                        'ipatokenradiusproxyuser')

                answer = self.api.Object['radiusproxy'].get_dn_if_exists(cl)
                entry_attrs['ipatokenradiusconfiglink'] = answer

        return dn

    def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
        assert isinstance(dn, DN)
        config = ldap.get_ipa_config()

        # Fetch the entry again to update memberof, mep data, etc updated
        # at the end of the transaction.
        newentry = ldap.get_entry(dn, ['*'])
        entry_attrs.update(newentry)

        if options.get('random', False):
            try:
                entry_attrs['randompassword'] = unicode(
                    getattr(context, 'randompassword'))
            except AttributeError:
                # if both randompassword and userpassword options were used
                pass

        self.post_common_callback(ldap, dn, entry_attrs, **options)
        return dn
Пример #5
0
class hbactest(Command):
    __doc__ = _('Simulate use of Host-based access controls')

    has_output = (
        output.summary,
        output.Output('warning', (list, tuple, NoneType),   _('Warning')),
        output.Output('matched', (list, tuple, NoneType),   _('Matched rules')),
        output.Output('notmatched', (list, tuple, NoneType), _('Not matched rules')),
        output.Output('error', (list, tuple, NoneType), _('Non-existent or invalid rules')),
        output.Output('value',  bool, _('Result of simulation'), ['no_display']),
    )

    takes_options = (
        Str('user',
            cli_name='user',
            label=_('User name'),
            primary_key=True,
        ),
        DeprecatedParam('sourcehost?'),
        Str('targethost',
            cli_name='host',
            label=_('Target host'),
        ),
        Str('service',
            cli_name='service',
            label=_('Service'),
        ),
        Str('rules*',
             cli_name='rules',
             label=_('Rules to test. If not specified, --enabled is assumed'),
             csv=True,
        ),
        Flag('nodetail?',
             cli_name='nodetail',
             label=_('Hide details which rules are matched, not matched, or invalid'),
        ),
        Flag('enabled?',
             cli_name='enabled',
             label=_('Include all enabled IPA rules into test [default]'),
        ),
        Flag('disabled?',
             cli_name='disabled',
             label=_('Include all disabled IPA rules into test'),
        ),
        Int('sizelimit?',
            label=_('Size Limit'),
            doc=_('Maximum number of rules to process when no --rules is specified'),
            flags=['no_display'],
            minvalue=0,
            autofill=False,
        ),
    )

    def canonicalize(self, host):
        """
        Canonicalize the host name -- add default IPA domain if that is missing
        """
        if host.find('.') == -1:
            return u'%s.%s' % (host, self.env.domain)
        return host

    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)['result']
        else:
            for rule in testrules:
                try:
                    hbacset.append(self.api.Command.hbacrule_show(rule)['result'])
                except:
                    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':
            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:
                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:
                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:
                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 (code, rule_name):
                    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
Пример #6
0
class hbactest(Command):
    __doc__ = _('Simulate use of Host-based access controls')

    has_output = (
        output.summary,
        output.Output('warning', (list, tuple, NoneType), _('Warning')),
        output.Output('matched', (list, tuple, NoneType), _('Matched rules')),
        output.Output('notmatched', (list, tuple, NoneType),
                      _('Not matched rules')),
        output.Output('error', (list, tuple, NoneType),
                      _('Non-existent or invalid rules')),
        output.Output('value', bool, _('Result of simulation'),
                      ['no_display']),
    )

    takes_options = (
        Str(
            'user',
            cli_name='user',
            label=_('User name'),
            primary_key=True,
        ),
        DeprecatedParam('sourcehost?'),
        Str(
            'targethost',
            cli_name='host',
            label=_('Target host'),
        ),
        Str(
            'service',
            cli_name='service',
            label=_('Service'),
        ),
        Str(
            'rules*',
            cli_name='rules',
            label=_('Rules to test. If not specified, --enabled is assumed'),
            csv=True,
        ),
        Flag(
            'nodetail?',
            cli_name='nodetail',
            label=_(
                'Hide details which rules are matched, not matched, or invalid'
            ),
        ),
        Flag(
            'enabled?',
            cli_name='enabled',
            label=_('Include all enabled IPA rules into test [default]'),
        ),
        Flag(
            'disabled?',
            cli_name='disabled',
            label=_('Include all disabled IPA rules into test'),
        ),
        Int(
            'sizelimit?',
            label=_('Size Limit'),
            doc=
            _('Maximum number of rules to process when no --rules is specified'
              ),
            flags=['no_display'],
            minvalue=0,
            autofill=False,
        ),
    )

    def canonicalize(self, host):
        """
        Canonicalize the host name -- add default IPA domain if that is missing
        """
        if host.find('.') == -1:
            return u'%s.%s' % (host, self.env.domain)
        return host

    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)['result']
        else:
            for rule in testrules:
                try:
                    hbacset.append(
                        self.api.Command.hbacrule_show(rule)['result'])
                except:
                    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:
                    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:
                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:
                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 (code, rule_name):
                    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