[Freeipa-devel] [PATCH] 0118 add support for subdomains
Sumit Bose
sbose at redhat.com
Fri Sep 20 15:40:05 UTC 2013
On Thu, Sep 19, 2013 at 10:08:37PM +0300, Alexander Bokovoy wrote:
> Hi!
>
> Attached patch adds IPA CLI to manage trust subdomains.
>
> ipa trust-domain-fetch <trust> -- fetch list of subdomains from AD side and add new ones to IPA
> ipa trust-domain-find <trust> -- show all available subdomains
> ipa trust-domain-del <trust> <domain> -- remove subdomain from IPA
> view about <trust>
> ipa trust-domain-mod <trust> <domain> -- modify subdomain parameters (work in progress)
>
> IPA KDC needs also information for authentication paths to subdomains in
> case they are not hierarchical under AD forest trust root. This
> information is managed via capaths section in krb5.conf. SSSD should be
> able to generate it once ticket https://fedorahosted.org/sssd/ticket/2093 is resolved.
I just want to let you know that I successfully tested you patch with
sssd in a setup with the hierarchical case, i.e. the DNS name of the
subdomain is a child of the DNS name of the forest root. I also didn't
need to change the realm-domain mapping
(https://fedorahosted.org/sssd/ticket/2080) in this case. I will test
again with a non-hierarchical setup.
bye,
Sumit
>
> part of https://fedorahosted.org/freeipa/ticket/3909
>
> The patch implements some dark magic to get around IPA framework
> limitations:
>
> -- CLI commands belong to 'trust' family but operate on 'subdomain' object
> -- 'subdomain' objects are stored under trust container, thus making
> container_dn dependent on a particular trust:
> cn=<subdomain>,cn=<trust>,cn=ad,cn=trusts,$SUFFIX
>
> The latter is a design decision since our KDC driver loads all objects
> with objectclass=ipaNTTrustedDomain from cn=ad,cn=trusts,$SUFFIX using
> subtree scope. With this design no changes were needed in ipa-kdb at all
> to support subdomains.
>
> --
> / Alexander Bokovoy
> >From bf6145368cd517557f9839586cae32160291964e Mon Sep 17 00:00:00 2001
> From: Alexander Bokovoy <abokovoy at redhat.com>
> Date: Wed, 18 Sep 2013 17:04:19 +0200
> Subject: [PATCH 5/5] trusts: support subdomains in a forest
>
> Add IPA CLI to manage trust subdomains.
>
> ipa trust-domain-fetch <trust> -- fetch list of subdomains from AD side and add new ones to IPA
> ipa trust-domain-find <trust> -- show all available subdomains
> ipa trust-domain-del <trust> <domain> -- remove subdomain from IPA view about <trust>
> ipa trust-domain-mod <trust> <domain> -- modify subdomain parameters (work in progress)
>
> IPA KDC needs also information for authentication paths to subdomains in case they
> are not hierarchical under AD forest trust root. This information is managed via capaths
> section in krb5.conf. SSSD should be able to generate it once
> ticket https://fedorahosted.org/sssd/ticket/2093 is resolved.
>
> part of https://fedorahosted.org/freeipa/ticket/3909
> ---
> API.txt | 67 ++++++++++++++++++
> ipalib/plugins/trust.py | 176 +++++++++++++++++++++++++++++++++++++++++++++++-
> ipaserver/dcerpc.py | 54 +++++++++++++++
> 3 files changed, 295 insertions(+), 2 deletions(-)
>
> diff --git a/API.txt b/API.txt
> index 761d1d1..a7c97e0 100644
> --- a/API.txt
> +++ b/API.txt
> @@ -3423,6 +3423,73 @@ option: Str('version?', exclude='webui')
> output: Output('result', <type 'dict'>, None)
> output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
> output: Output('value', <type 'unicode'>, None)
> +command: trust_domain_create
> +args: 2,9,3
> +arg: Str('trust?')
> +arg: Str('cn?', cli_name='domain', primary_key=True)
> +option: Str('addattr*', cli_name='addattr', exclude='webui')
> +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
> +option: Str('ipantflatname', attribute=True, cli_name='flat_name', multivalue=False, required=False)
> +option: Str('ipanttrusteddomainsid', attribute=True, cli_name='sid', multivalue=False, required=False)
> +option: Str('ipanttrustpartner?')
> +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
> +option: Str('setattr*', cli_name='setattr', exclude='webui')
> +option: StrEnum('trust_type', autofill=True, cli_name='type', default=u'ad', values=(u'ad',))
> +option: Str('version?', exclude='webui')
> +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
> +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
> +output: Output('value', <type 'unicode'>, None)
> +command: trust_domain_del
> +args: 2,2,3
> +arg: Str('trust?')
> +arg: Str('cn?', cli_name='domain', primary_key=True)
> +option: Flag('continue', autofill=True, cli_name='continue', default=False)
> +option: Str('version?', exclude='webui')
> +output: Output('result', <type 'dict'>, None)
> +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
> +output: Output('value', <type 'unicode'>, None)
> +command: trust_domain_fetch
> +args: 1,4,2
> +arg: Str('trust?')
> +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
> +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
> +option: Flag('rights', autofill=True, default=False)
> +option: Str('version?', exclude='webui')
> +output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
> +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
> +command: trust_domain_find
> +args: 2,8,4
> +arg: Str('criteria?', noextrawhitespace=False)
> +arg: Str('trust?')
> +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
> +option: Str('cn?', cli_name='domain', primary_key=True)
> +option: Str('ipantflatname', attribute=True, autofill=False, cli_name='flat_name', multivalue=False, query=True, required=False)
> +option: Str('ipanttrusteddomainsid', attribute=True, autofill=False, cli_name='sid', multivalue=False, query=True, required=False)
> +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
> +option: Int('sizelimit?', autofill=False, minvalue=0)
> +option: Int('timelimit?', autofill=False, minvalue=0)
> +option: Str('version?', exclude='webui')
> +output: Output('count', <type 'int'>, None)
> +output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
> +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
> +output: Output('truncated', <type 'bool'>, None)
> +command: trust_domain_mod
> +args: 2,10,3
> +arg: Str('trust?')
> +arg: Str('cn?', cli_name='domain', primary_key=True)
> +option: Str('addattr*', cli_name='addattr', exclude='webui')
> +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
> +option: Str('delattr*', cli_name='delattr', exclude='webui')
> +option: Str('ipantflatname', attribute=True, autofill=False, cli_name='flat_name', multivalue=False, required=False)
> +option: Str('ipanttrusteddomainsid', attribute=True, autofill=False, cli_name='sid', multivalue=False, required=False)
> +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
> +option: Flag('rights', autofill=True, default=False)
> +option: Str('setattr*', cli_name='setattr', exclude='webui')
> +option: StrEnum('trust_type', autofill=True, cli_name='type', default=u'ad', values=(u'ad',))
> +option: Str('version?', exclude='webui')
> +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
> +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
> +output: Output('value', <type 'unicode'>, None)
> command: trust_find
> args: 1,11,4
> arg: Str('criteria?', noextrawhitespace=False)
> diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py
> index 3c117b4..6ffe119 100644
> --- a/ipalib/plugins/trust.py
> +++ b/ipalib/plugins/trust.py
> @@ -245,7 +245,7 @@ def make_trust_dn(env, trust_type, dn):
> assert isinstance(dn, DN)
> if trust_type in trust.trust_types:
> container_dn = DN(('cn', trust_type), env.container_trusts, env.basedn)
> - return DN(dn[0], container_dn)
> + return DN(dn, container_dn)
> return dn
>
> class trust_add(LDAPCreate):
> @@ -738,7 +738,7 @@ class trust_show(LDAPRetrieve):
> def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
> assert isinstance(dn, DN)
> if 'trust_show_type' in options:
> - return make_trust_dn(self.env, options['trust_show_type'], dn)
> + return make_trust_dn(self.env, options['trust_show_type'], DN(dn[0]))
>
> return dn
>
> @@ -1078,3 +1078,175 @@ class sidgen_was_run(Command):
> return dict(result=True)
>
> api.register(sidgen_was_run)
> +
> +class subdomain(LDAPObject):
> + """
> + Object representing a domain of the AD trust.
> + """
> + trust_type_idx = {'2':u'ad'}
> + object_name = _('trust domain')
> + object_name_plural = _('trust domains')
> + object_class = ['ipaNTTrustedDomain']
> + default_attributes = ['cn', 'ipantflatname', 'ipanttrusteddomainsid', 'ipanttrustdirection', 'ipanttrustpartner']
> + search_display_attributes = ['cn', 'ipantflatname', 'ipanttrusteddomainsid', ]
> +
> + label = _('Trusted domains')
> + label_singular = _('Trusted domain')
> +
> + subdomain_args = (
> + Str('trust?',
> + label=_('AD trust'),
> + flags=('virtual_attribute',),
> + ),
> + Str('cn?',
> + label=_('Domain name'),
> + cli_name='domain',
> + primary_key=True,
> + flags=('req_update','req_create',),
> + ),
> + )
> +
> + takes_params = (
> + Str('ipantflatname?',
> + cli_name='flat_name',
> + label=_('Domain NetBIOS name'),
> + ),
> + Str('ipanttrusteddomainsid?',
> + cli_name='sid',
> + label=_('Domain Security Identifier'),
> + ),
> + )
> +
> + container_dn = api.env.container_trusts
> + def get_dn(self, *keys, **kwargs):
> + if len(keys) == 1:
> + return self.api.Object.trust.get_dn(*keys, **kwargs)
> + dn=make_trust_dn(self.env, getattr(kwargs, 'trust_type', u'ad'), DN(('cn', keys[1]), ('cn', keys[0])))
> + return dn
> +
> +api.register(subdomain)
> +
> +class trust_domain_find(LDAPSearch):
> + __doc__ = _('Search subdomains of the trust')
> +
> + obj_name = 'subdomain'
> +
> + takes_args = subdomain.subdomain_args[0]
> + takes_options = LDAPSearch.takes_options + (subdomain.subdomain_args[1],)
> + def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
> + assert isinstance(base_dn, DN)
> + if not args[0]:
> + raise errors.ValidationError(name=_('AD trust'), error=_('Trust name is required to search subdomains'))
> +
> + base_dn = DN(self.api.Command.trust_show(args[0])['result']['dn'])
> + return (filters, base_dn, ldap.SCOPE_SUBTREE)
> +api.register(trust_domain_find)
> +
> +class trust_domain_mod(LDAPUpdate):
> + __doc__ = _('Modify subdomain of the trust')
> +
> + obj_name = 'subdomain'
> +
> + takes_args = subdomain.subdomain_args
> + takes_options = LDAPUpdate.takes_options + (_trust_type_option,)
> + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
> + if len(keys) != 2:
> + raise errors.ValidationError(name=_('AD trust'), error=_('Trust name and subdomain name are required to modify the subdomain'))
> + return dn
> +api.register(trust_domain_mod)
> +
> +class trust_domain_create(LDAPCreate):
> + __doc__ = _('Create subdomain of the trust')
> + NO_CLI = True
> + obj_name = 'subdomain'
> +
> + takes_args = subdomain.subdomain_args
> + takes_options = LDAPCreate.takes_options + (_trust_type_option,
> + Str('ipanttrustpartner?',
> + label=_('Trusted domain partner'),
> + ),
> + )
> +
> + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
> + if len(keys) != 2:
> + raise errors.ValidationError(name=_('AD trust'), error=_('Trust name and subdomain name are required to create the subdomain'))
> + assert isinstance(dn, DN)
> + if 'ipanttrustpartner' in options:
> + entry_attrs['ipanttrustpartner'] = [options['ipanttrustpartner']]
> + self.log.error('trust_domain_create(%s,%s), entry attrs %s' % (keys,options,entry_attrs))
> + return dn
> +api.register(trust_domain_create)
> +
> +class trust_domain_del(LDAPDelete):
> + __doc__ = _('Delete a subdomain of the trust.')
> +
> + obj_name = 'subdomain'
> + msg_summary = _('Deleted subdomain "%(value)s"')
> +
> + takes_args = subdomain.subdomain_args
> + def pre_callback(self, ldap, dn, *keys, **options):
> + if len(keys) != 2:
> + raise errors.ValidationError(name=_('AD trust'), error=_('Trust name and subdomain name are required to delete the subdomain'))
> + try:
> + result = self.api.Command.trust_show(keys[0], raw=True)
> + except errors.NotFound, e:
> + self.obj.handle_not_found(*keys)
> + options['trust_type'] = self.obj.trust_type_idx[result['result']['ipanttrusttype'][0]]
> + return make_trust_dn(self.env, options['trust_type'], DN(('cn', keys[1]), ('cn', keys[0])))
> +api.register(trust_domain_del)
> +
> +class trust_domain_fetch(LDAPRetrieve):
> + __doc__ = _('Refresh list of subdomains associated with the trust')
> + obj_name = 'subdomain'
> +
> + takes_args = (subdomain.subdomain_args[0])
> +
> + has_output = (
> + output.ListOfEntries('result'),
> + output.summary
> + )
> + def execute(self, *keys, **options):
> + if not _bindings_installed:
> + raise errors.NotFound(
> + name=_('AD Trust setup'),
> + reason=_(
> + 'Cannot perform join operation without Samba 4 support '
> + 'installed. Make sure you have installed server-trust-ad '
> + 'sub-package of IPA'
> + )
> + )
> + if len(keys) != 1:
> + raise errors.ValidationError(name=_('AD trust'), error=_('Trust name is required to search subdomains'))
> +
> + trust = self.api.Command.trust_show(keys[0], raw=True)['result']
> +
> + trustinstance = ipaserver.dcerpc.TrustDomainJoins(self.api)
> + if not trustinstance.configured:
> + raise errors.NotFound(
> + name=_('AD Trust setup'),
> + reason=_(
> + 'Cannot perform join operation without own domain '
> + 'configured. Make sure you have run ipa-adtrust-install '
> + 'on the IPA server first'
> + )
> + )
> + domains = ipaserver.dcerpc.fetch_domains(self.api, trustinstance.local_flatname, trust['cn'][0])
> + result = dict(result=[])
> + if not domains:
> + result['summary'] = unicode(_('No sudomains were detected during refresh'))
> + return result
> +
> + for dom in domains:
> + dom['trust_type'] = self.obj.trust_type_idx[trust['ipanttrusttype'][0]]
> + try:
> + res = self.api.Command.trust_domain_create(trust['cn'][0], **dom)
> + result['result'].append(res['result'])
> + except errors.DuplicateEntry:
> + # Ignore updating duplicate entries
> + pass
> + if len(result['result']) > 0:
> + result['summary'] = unicode(_('Subdomains successfully refreshed'))
> + else:
> + result['summary'] = unicode(_('No new subdomains were found'))
> + return result
> +api.register(trust_domain_fetch)
> diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
> index 33e7e07..fcfedc4 100644
> --- a/ipaserver/dcerpc.py
> +++ b/ipaserver/dcerpc.py
> @@ -992,6 +992,60 @@ class TrustDomainInstance(object):
> return True
> return False
>
> +def fetch_domains(api, mydomain, trustdomain):
> + trust_flags = dict(
> + NETR_TRUST_FLAG_IN_FOREST = 0x00000001,
> + NETR_TRUST_FLAG_OUTBOUND = 0x00000002,
> + NETR_TRUST_FLAG_TREEROOT = 0x00000004,
> + NETR_TRUST_FLAG_PRIMARY = 0x00000008,
> + NETR_TRUST_FLAG_NATIVE = 0x00000010,
> + NETR_TRUST_FLAG_INBOUND = 0x00000020,
> + NETR_TRUST_FLAG_MIT_KRB5 = 0x00000080,
> + NETR_TRUST_FLAG_AES = 0x00000100)
> +
> + trust_attributes = dict(
> + NETR_TRUST_ATTRIBUTE_NON_TRANSITIVE = 0x00000001,
> + NETR_TRUST_ATTRIBUTE_UPLEVEL_ONLY = 0x00000002,
> + NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN = 0x00000004,
> + NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE = 0x00000008,
> + NETR_TRUST_ATTRIBUTE_CROSS_ORGANIZATION = 0x00000010,
> + NETR_TRUST_ATTRIBUTE_WITHIN_FOREST = 0x00000020,
> + NETR_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL = 0x00000040)
> +
> + domval = DomainValidator(api)
> + (ccache_name, principal) = domval.kinit_as_http(trustdomain)
> + if ccache_name:
> + with installutils.private_ccache(path=ccache_name):
> + td = TrustDomainInstance('')
> + td.parm.set('workgroup', mydomain)
> + td.creds = credentials.Credentials()
> + td.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS)
> + td.creds.guess(td.parm)
> + netrc = net.Net(creds=td.creds, lp=td.parm)
> + try:
> + result = netrc.finddc(domain=trustdomain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
> + except RuntimeError, e:
> + raise assess_dcerpc_exception(message=str(e))
> + if not result:
> + return None
> + td.retrieve(unicode(result.pdc_dns_name))
> +
> + netr_pipe = netlogon.netlogon(td.binding, td.parm, td.creds)
> + domains = netr_pipe.netr_DsrEnumerateDomainTrusts(td.binding, 1)
> +
> + result = []
> + for t in domains.array:
> + if ((t.trust_attributes & trust_attributes['NETR_TRUST_ATTRIBUTE_WITHIN_FOREST']) and
> + (t.trust_flags & trust_flags['NETR_TRUST_FLAG_IN_FOREST'])):
> + res = dict()
> + res['cn'] = unicode(t.dns_name)
> + res['ipantflatname'] = unicode(t.netbios_name)
> + res['ipanttrusteddomainsid'] = unicode(t.sid)
> + res['ipanttrustpartner'] = res['cn']
> + result.append(res)
> + return result
> +
> +
> class TrustDomainJoins(object):
> def __init__(self, api):
> self.api = api
> --
> 1.8.3.1
>
> _______________________________________________
> Freeipa-devel mailing list
> Freeipa-devel at redhat.com
> https://www.redhat.com/mailman/listinfo/freeipa-devel
More information about the Freeipa-devel
mailing list