[Freeipa-devel] [PATCH] 0118 add support for subdomains

Alexander Bokovoy abokovoy at redhat.com
Fri Oct 4 05:59:45 UTC 2013


On Fri, 04 Oct 2013, Alexander Bokovoy wrote:
>On Thu, 03 Oct 2013, Martin Kosek wrote:
>>On 10/03/2013 03:10 PM, Alexander Bokovoy wrote:
>>>On Wed, 02 Oct 2013, Sumit Bose wrote:
>>>>>Please note that I did not test with more than 1 subdomain, since I
>>>>>do not have more ADs available.
>>>>>
>>>>
>>>>I have done some testing as well and the patches are working as expected
>>>>except the trustdomain-disable issue Tomas mentioned. But I think it
>>>>would be sufficient to add a comment to the release notes and fix this
>>>>with the next release to not delay this release anymore.
>>>>
>>>>The patches are also working for trusts which were added with older
>>>>releases. So ACK from my side for the functional part.
>>>New patchset is attached. I've fixed all outstanding issues and
>>>implemented proper SID filtering for subdomains. In addition, I've
>>>added MS-PAC cache eviction when we change blacklists from IPA side
>>>and forced removal of the domain from SID blacklist if the domain is
>>>being removed by trustdomain-del.
>>>
>>
>>1) Minor issue in 0118:
>>
>>+        if keys[0].lower() == keys[1].lower():
>>+            raise errors.ValidationError(name='trustdomain_enable',
>>+                error=_("Root domain of the trust is always enabled for the
>>existing trust"))
>>
>>The error message looks weird (double trustdomain_enable)
>>
>># ipa trustdomain-enable realm domain
>>ipa: ERROR: invalid 'trustdomain_enable': Root domain of the trust is always
>>enabled for the existing trust
>>
>>I would rather do something like
>>
>>+            raise errors.ValidationError(name='domain',
>>
>>
>>2) trustconfig-enable and trustconfig-disable should use standard output like
>>other enable/disable methods. See user-enable/user-disable for example. Current
>>situation puts all the authoritative information to summary which:
>>a) Does not look nice in terminal
>># ipa trustdomain-disable very.long.long.long.realm very.long.long.long.domain
>>----------------------------------------------------------------------------------------------------------------------------
>>Domain very.long.long.long.domain of trust very.long.long.long.realm is not
>>allowed to access IPA resources
>>----------------------------------------------------------------------------------------------------------------------------
>>b) How am I supposed to parse an information about the result if all I get is a
>>text in summary? Using standard errors and output values will allow easier
>>consumption of the API later (like in Web UI).
>>
>>I am attaching a patch (0001) how to make it consistent with other
>>enable/disable commands. Example:
>>
>># ipa trustdomain-disable realm domain
>>ipa: ERROR: This entry is already disabled
>>
>># ipa trustdomain-enable realm domain
>>-----------------------------
>>Enabled trust domain "domain"
>>-----------------------------
>>
>>3) Let's use standard primary key for the trustdomain object. This will let us
>>overcome some hacks and also let us use handle_not_found method - patch
>>attached (0002).
>>
>>0002 also changes some ValidationError errors to standard errors, just for
>>being consistent with the rest of the API.
>>
>>Note that in order to make primary_key=True, I had to enhance trustdomain_del
>>command to manage multiple domains.
>>
>>
>>I think these API fixes are a must, it would be very hard to amend the API
>>later. If these patches are squashed to your 0118, it would be ACK from me to
>>the Python side. I will let C parts and a functional test to Sumit's mighty hands.
>Thanks. I've merged these changes, along with a API.txt correction. In
>my tests these worked fine.
>
>I'll resend 0118 shortly.
New edition of 0118 attached.

-- 
/ Alexander Bokovoy
-------------- next part --------------
>From 1e5d4090d53ce0e889f05359685efd8819771826 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 2/8] trusts: support subdomains in a forest

Add IPA CLI to manage trust domains.

ipa trust-fetch-domains <trust>      -- fetch list of subdomains from AD side and add new ones to IPA
ipa trustdomain-find <trust>         -- show all available domains
ipa trustdomain-del <trust> <domain> -- remove domain from IPA view about <trust>
ipa trustdomain-enable <trust> <domain> -- allow users from trusted domain to access resources in IPA
ipa trustdomain-disable <trust> <domain> -- disable access to resources in IPA from trusted domain

By default all discovered trust domains are allowed to access IPA resources

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                 |  88 +++++++++++++
 ipalib/plugins/trust.py | 329 ++++++++++++++++++++++++++++++++++++++++--------
 ipaserver/dcerpc.py     |  54 ++++++++
 3 files changed, 420 insertions(+), 51 deletions(-)

diff --git a/API.txt b/API.txt
index 761d1d1..40871f6 100644
--- a/API.txt
+++ b/API.txt
@@ -3423,6 +3423,17 @@ 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_fetch_domains
+args: 1,4,4
+arg: Str('cn', attribute=True, cli_name='realm', multivalue=False, primary_key=True, query=True, required=True)
+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: 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_find
 args: 1,11,4
 arg: Str('criteria?', noextrawhitespace=False)
@@ -3497,6 +3508,83 @@ 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: trustdomain_add
+args: 2,9,3
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='domain', multivalue=False, primary_key=True, required=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', attribute=True, cli_name='ipanttrustpartner', multivalue=False, required=False)
+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: trustdomain_del
+args: 2,2,3
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='domain', multivalue=True, primary_key=True, query=True, required=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: trustdomain_disable
+args: 2,1,3
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='domain', multivalue=False, primary_key=True, query=True, required=True)
+option: Str('version?', exclude='webui')
+output: Output('result', <type 'bool'>, None)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('value', <type 'unicode'>, None)
+command: trustdomain_enable
+args: 2,1,3
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='domain', multivalue=False, primary_key=True, query=True, required=True)
+option: Str('version?', exclude='webui')
+output: Output('result', <type 'bool'>, None)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('value', <type 'unicode'>, None)
+command: trustdomain_find
+args: 2,10,4
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+arg: Str('criteria?', noextrawhitespace=False)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Str('cn', attribute=True, autofill=False, cli_name='domain', multivalue=False, primary_key=True, query=True, required=False)
+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: Str('ipanttrustpartner', attribute=True, autofill=False, cli_name='ipanttrustpartner', multivalue=False, query=True, required=False)
+option: Flag('pkey_only?', autofill=True, default=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: trustdomain_mod
+args: 2,11,3
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+arg: Str('cn', attribute=True, cli_name='domain', multivalue=False, primary_key=True, query=True, required=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: Str('ipanttrustpartner', attribute=True, autofill=False, cli_name='ipanttrustpartner', 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: user_add
 args: 1,35,3
 arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, required=True)
diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py
index 3d05029..82b2c96 100644
--- a/ipalib/plugins/trust.py
+++ b/ipalib/plugins/trust.py
@@ -181,6 +181,13 @@ def trust_status_string(level):
     string = _trust_status_dict.get(level, _trust_type_dict_unknown)
     return unicode(string)
 
+def make_trust_dn(env, trust_type, dn):
+    assert isinstance(dn, DN)
+    if trust_type:
+        container_dn = DN(('cn', trust_type), env.container_trusts, env.basedn)
+        return DN(dn, container_dn)
+    return dn
+
 class trust(LDAPObject):
     """
     Trust object.
@@ -195,7 +202,8 @@ class trust(LDAPObject):
         'ipantauthtrustoutgoing', 'ipanttrustauthincoming', 'ipanttrustforesttrustinfo',
         'ipanttrustposixoffset', 'ipantsupportedencryptiontypes' ]
     search_display_attributes = ['cn', 'ipantflatname',
-                                 'ipanttrusteddomainsid', 'ipanttrusttype' ]
+                                 'ipanttrusteddomainsid', 'ipanttrusttype',
+                                 'ipantsidblacklistincoming', 'ipantsidblacklistoutgoing' ]
 
     label = _('Trusts')
     label_singular = _('Trust')
@@ -241,12 +249,26 @@ class trust(LDAPObject):
                     raise errors.ValidationError(name=attr,
                             error=_("invalid SID: %(value)s") % dict(value=value))
 
-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
+    def get_dn(self, *keys, **kwargs):
+        sdn = map(lambda x: ('cn', x), keys)
+        sdn.reverse()
+        trust_type = kwargs.get('trust_type')
+        if trust_type is None:
+            ldap = self.backend
+            filter = ldap.make_filter({'objectclass': ['ipaNTTrustedDomain'], 'cn': [keys[-1]]})
+            filter = ldap.combine_filters((filter, "ipaNTSIDBlacklistIncoming=*"), rules=ldap.MATCH_ALL)
+            try:
+                result = ldap.get_entries(DN(self.container_dn, self.env.basedn),
+                                          ldap.SCOPE_SUBTREE, filter, [''])
+            except errors.NotFound:
+                trust_type = u'ad'
+            else:
+                if len(result) > 1:
+                    raise errors.OnlyOneValueAllowed(attr='trust domain')
+                return result[0].dn
+
+        dn=make_trust_dn(self.env, trust_type, DN(*sdn))
+        return dn
 
 class trust_add(LDAPCreate):
     __doc__ = _('''
@@ -589,10 +611,13 @@ sides.
     def execute_ad(self, full_join, *keys, **options):
         # Join domain using full credentials and with random trustdom
         # secret (will be generated by the join method)
-        try:
-            api.Command['trust_show'](keys[-1])
+
+        # First see if the trust is already in place
+        # Force retrieval of the trust object by not passing trust_type
+        dn = self.obj.get_dn(keys[-1])
+        if dn:
             summary = _('Re-established trust to domain "%(value)s"')
-        except errors.NotFound:
+        else:
             summary = self.msg_summary
 
         # 1. Full access to the remote domain. Use admin credentials and
@@ -665,14 +690,6 @@ class trust_del(LDAPDelete):
 
     msg_summary = _('Deleted trust "%(value)s"')
 
-    def pre_callback(self, ldap, dn, *keys, **options):
-        assert isinstance(dn, DN)
-        try:
-            result = self.api.Command.trust_show(keys[-1])
-        except errors.NotFound, e:
-            self.obj.handle_not_found(*keys)
-        return result['result']['dn']
-
 class trust_mod(LDAPUpdate):
     __doc__ = _("""
     Modify a trust (for future use).
@@ -686,16 +703,10 @@ class trust_mod(LDAPUpdate):
 
     def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
         assert isinstance(dn, DN)
-        result = None
-        try:
-            result = self.api.Command.trust_show(keys[-1])
-        except errors.NotFound, e:
-            self.obj.handle_not_found(*keys)
 
         self.obj.validate_sid_blacklists(entry_attrs)
 
-        # TODO: we found the trust object, now modify it
-        return result['result']['dn']
+        return dn
 
 class trust_find(LDAPSearch):
     __doc__ = _('Search for trusts.')
@@ -709,8 +720,10 @@ class trust_find(LDAPSearch):
     # Since all trusts types are stored within separate containers under 'cn=trusts',
     # search needs to be done on a sub-tree scope
     def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
-        assert isinstance(base_dn, DN)
-        return (filters, base_dn, ldap.SCOPE_SUBTREE)
+        # list only trust, not trust domains
+        trust_filter = '(ipaNTSIDBlacklistIncoming=*)'
+        filter = ldap.combine_filters((filters, trust_filter), rules=ldap.MATCH_ALL)
+        return (filter, base_dn, ldap.SCOPE_SUBTREE)
 
     def post_callback(self, ldap, entries, truncated, *args, **options):
         if options.get('pkey_only', False):
@@ -731,30 +744,6 @@ class trust_show(LDAPRetrieve):
     has_output_params = LDAPRetrieve.has_output_params + trust_output_params +\
                         (Str('ipanttrusttype'), Str('ipanttrustdirection'))
 
-    def execute(self, *keys, **options):
-        error = None
-        result = None
-        for trust_type in trust.trust_types:
-            options['trust_show_type'] = trust_type
-            try:
-                result = super(trust_show, self).execute(*keys, **options)
-            except errors.NotFound, e:
-                result = None
-                error = e
-            if result:
-                break
-        if error or not result:
-            self.obj.handle_not_found(*keys)
-
-        return result
-
-    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 dn
-
     def post_callback(self, ldap, dn, entry_attrs, *keys, **options):
 
         # Translate ipanttrusttype to trusttype
@@ -1091,3 +1080,241 @@ class sidgen_was_run(Command):
         return dict(result=True)
 
 api.register(sidgen_was_run)
+
+class trustdomain(LDAPObject):
+    """
+    Object representing a domain of the AD trust.
+    """
+    parent_object = 'trust'
+    trust_type_idx = {'2':u'ad'}
+    object_name = _('trust domain')
+    object_name_plural = _('trust domains')
+    object_class = ['ipaNTTrustedDomain']
+    default_attributes = ['cn', 'ipantflatname', 'ipanttrusteddomainsid', 'ipanttrustpartner']
+    search_display_attributes = ['cn', 'ipantflatname', 'ipanttrusteddomainsid', ]
+
+    label = _('Trusted domains')
+    label_singular = _('Trusted domain')
+
+    takes_params = (
+        Str('cn',
+            label=_('Domain name'),
+            cli_name='domain',
+            primary_key=True
+        ),
+        Str('ipantflatname?',
+            cli_name='flat_name',
+            label=_('Domain NetBIOS name'),
+        ),
+        Str('ipanttrusteddomainsid?',
+            cli_name='sid',
+            label=_('Domain Security Identifier'),
+        ),
+        Str('ipanttrustpartner?',
+            label=_('Trusted domain partner'),
+            flags=['no_display', 'no_option'],
+        ),
+    )
+
+    # LDAPObject.get_dn() only passes all but last element of keys and no kwargs
+    # to the parent object's get_dn() no matter what you pass to it. Make own get_dn()
+    # as we really need all elements to construct proper dn.
+    def get_dn(self, *keys, **kwargs):
+        sdn = map(lambda x: ('cn', x), keys)
+        sdn.reverse()
+        trust_type = kwargs.get('trust_type')
+        if not trust_type:
+            trust_type=u'ad'
+
+        dn=make_trust_dn(self.env, trust_type, DN(*sdn))
+        return dn
+api.register(trustdomain)
+
+class trustdomain_find(LDAPSearch):
+    __doc__ = _('Search domains of the trust')
+
+    def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
+        return (filters, base_dn, ldap.SCOPE_SUBTREE)
+api.register(trustdomain_find)
+
+class trustdomain_mod(LDAPUpdate):
+    __doc__ = _('Modify trustdomain of the trust')
+
+    NO_CLI = True
+    takes_options = LDAPUpdate.takes_options + (_trust_type_option,)
+api.register(trustdomain_mod)
+
+class trustdomain_add(LDAPCreate):
+    __doc__ = _('Allow access from the trusted domain')
+    NO_CLI = True
+
+    takes_options = LDAPCreate.takes_options + (_trust_type_option,)
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+        if 'ipanttrustpartner' in options:
+            entry_attrs['ipanttrustpartner'] = [options['ipanttrustpartner']]
+        return dn
+api.register(trustdomain_add)
+
+class trustdomain_del(LDAPDelete):
+    __doc__ = _('Remove infromation about the domain associated with the trust.')
+
+    msg_summary = _('Removed information about the trusted domain "%(value)s"')
+
+    def execute(self, *keys, **options):
+        # Note that pre-/post- callback handling for LDAPDelete is causing pre_callback
+        # to always receive empty keys. We need to catch the case when root domain is being deleted
+
+        for domain in keys[1]:
+            if keys[0].lower() == domain:
+                raise errors.ValidationError(name='domain',
+                    error=_("cannot delete root domain of the trust, use trust-del to delete the trust itself"))
+            try:
+                res = api.Command.trustdomain_enable(keys[0], domain)
+            except errors.AlreadyActive:
+                pass
+        result = super(trustdomain_del, self).execute(*keys, **options)
+        result['value'] = u','.join(keys[1])
+        return result
+
+
+api.register(trustdomain_del)
+
+
+def fetch_domains_from_trust(self, trustinstance, trust_entry):
+    trust_name = trust_entry['cn'][0]
+    domains = ipaserver.dcerpc.fetch_domains(self.api, trustinstance.local_flatname, trust_name)
+    result = []
+    if not domains:
+        return None
+
+    for dom in domains:
+        dom['trust_type'] = u'ad'
+        try:
+            name = dom['cn']
+            del dom['cn']
+            if 'all' in options:
+                dom['all'] = options['all']
+            if 'raw' in options:
+                dom['raw'] = options['raw']
+            res = self.api.Command.trustdomain_add(trust_name, name, **dom)
+            result.append(res['result'])
+        except errors.DuplicateEntry:
+            # Ignore updating duplicate entries
+            pass
+    return result
+
+class trust_fetch_domains(LDAPRetrieve):
+    __doc__ = _('Refresh list of the domains associated with the trust')
+
+    has_output = output.standard_list_of_entries
+
+    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'
+                )
+            )
+        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 = fetch_domains_from_trust(self, trustinstance, trust)
+        result = dict()
+
+        if len(domains) > 0:
+            result['summary'] = unicode(_('List of trust domains successfully refreshed'))
+        else:
+            result['summary'] = unicode(_('No new trust domains were found'))
+
+        result['result'] = domains
+        result['count'] = len(domains)
+        result['truncated'] = False
+        return result
+
+api.register(trust_fetch_domains)
+
+class trustdomain_enable(LDAPQuery):
+    __doc__ = _('Allow use of IPA resources by the domain of the trust')
+
+    has_output = output.standard_value
+    msg_summary = _('Enabled trust domain "%(value)s"')
+
+    def execute(self, *keys, **options):
+        ldap = self.api.Backend.ldap2
+
+        if keys[0].lower() == keys[1].lower():
+            raise errors.ValidationError(name='domain',
+                error=_("Root domain of the trust is always enabled for the existing trust"))
+        try:
+            trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad')
+            trust_entry = ldap.get_entry(trust_dn)
+        except errors.NotFound:
+            raise self.api.Object[self.obj.parent_object].handle_not_found(keys[0])
+
+        dn = self.obj.get_dn(keys[0], keys[1], trust_type=u'ad')
+        try:
+            entry = ldap.get_entry(dn)
+            sid = entry['ipanttrusteddomainsid'][0]
+            if sid in trust_entry['ipantsidblacklistincoming']:
+                trust_entry['ipantsidblacklistincoming'].remove(sid)
+                ldap.update_entry(trust_entry)
+            else:
+                raise errors.AlreadyActive()
+        except errors.NotFound:
+            self.obj.handle_not_found(*keys)
+
+        return dict(
+            result=True,
+            value=keys[1],
+        )
+
+api.register(trustdomain_enable)
+
+class trustdomain_disable(LDAPQuery):
+    __doc__ = _('Disable use of IPA resources by the domain of the trust')
+
+    has_output = output.standard_value
+    msg_summary = _('Disabled trust domain "%(value)s"')
+
+    def execute(self, *keys, **options):
+        ldap = self.api.Backend.ldap2
+
+        if keys[0].lower() == keys[1].lower():
+            raise errors.ValidationError(name='domain',
+                error=_("cannot disable root domain of the trust, use trust-del to delete the trust itself"))
+        try:
+            trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad')
+            trust_entry = ldap.get_entry(trust_dn)
+        except errors.NotFound:
+            self.api.Object[self.obj.parent_object].handle_not_found(keys[0])
+
+        dn = self.obj.get_dn(keys[0], keys[1], trust_type=u'ad')
+        try:
+            entry = ldap.get_entry(dn)
+            sid = entry['ipanttrusteddomainsid'][0]
+            if not (sid in trust_entry['ipantsidblacklistincoming']):
+                trust_entry['ipantsidblacklistincoming'].append(sid)
+                ldap.update_entry(trust_entry)
+            else:
+                raise errors.AlreadyInactive()
+        except errors.NotFound:
+            self.obj.handle_not_found(*keys)
+
+        return dict(
+            result=True,
+            value=keys[1],
+        )
+
+api.register(trustdomain_disable)
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index cc9e7be..1c4f4a6 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -1002,6 +1002,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



More information about the Freeipa-devel mailing list