On Mon, 23 Sep 2013, Alexander Bokovoy wrote:
On Mon, 23 Sep 2013, Martin Kosek wrote:
However, we don't have trust type available so it needs to discovered
every time. This doesn't play well with the framework, it is simply not
expecting dynamic containers.

This doesn't sound like a big obstacle to me. Right now the trust_type lookup
is done in trust_show.execute() for some reason, which is not the best place to
do it IMHO. Doing it in trust.get_dn() instead should simplify things enough to
make parent_object work.

Yup, get_dn() is the method where object DN lookup should be done. See for
example host.py plugin get_dn method, we also do a dynamic lookup for correct
host name.
I'll see if that would work.

the best way to implement dynamic DN gathering is the get_dn() method. That
way, it could be implemented in one place and all commands could take advantage
of it instead of re-implementing it several times in pre_callback - this is
just hackish.
I'd suggest you look into the code. The commands use pre_callback for a
different purpose than implementing dynamic DN gathering.

I think it would have been very useful to have a design page before sending a
patch. It is then easier to make design decisions without having to dig into
the patch.
The design page is there for long time:
http://www.freeipa.org/page/V3/Transitive_Trusts
Ok, here is new version of the patch and updated version of my 0117
patch as Sumit noticed I've sent wrong version.



--
/ Alexander Bokovoy
>From e2b43dae3b413fed303e7bde08e7868a9487ccbe Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <aboko...@redhat.com>
Date: Wed, 11 Sep 2013 21:34:55 +0300
Subject: [PATCH 1/2] ipaserver/dcerpc.py: populate forest trust information
 using realmdomains

Use realmdomains information to prepopulate forest trust info. As result,
all additional domains should now be enabled from the beginning, unless they
really conflict with existing DNS domains on AD side.

https://fedorahosted.org/freeipa/ticket/3919
---
 ipaserver/dcerpc.py | 113 +++++++++++++++++++++++++++++++++++++++++++---------
 1 file changed, 95 insertions(+), 18 deletions(-)

diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index bd8f5aa..c24230b 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -39,7 +39,7 @@ import uuid
 from samba import param
 from samba import credentials
 from samba.dcerpc import security, lsa, drsblobs, nbt, netlogon
-from samba.ndr import ndr_pack
+from samba.ndr import ndr_pack, ndr_print
 from samba import net
 import samba
 import random
@@ -684,6 +684,12 @@ class DomainValidator(object):
         self._info[domain] = info
         return info
 
+def string_to_array(what):
+    blob = [0] * len(what)
+
+    for i in range(len(what)):
+        blob[i] = ord(what[i])
+    return blob
 
 class TrustDomainInstance(object):
 
@@ -698,6 +704,7 @@ class TrustDomainInstance(object):
         self._pipe = None
         self._policy_handle = None
         self.read_only = False
+        self.ftinfo_records = None
 
     def __gen_lsa_connection(self, binding):
        if self.creds is None:
@@ -827,12 +834,6 @@ class TrustDomainInstance(object):
         def arcfour_encrypt(key, data):
             c = RC4.RC4(key)
             return c.update(data)
-        def string_to_array(what):
-            blob = [0] * len(what)
-
-            for i in range(len(what)):
-                blob[i] = ord(what[i])
-            return blob
 
         password_blob = string_to_array(trustdom_secret.encode('utf-16-le'))
 
@@ -876,6 +877,53 @@ class TrustDomainInstance(object):
         self.auth_info = auth_info
 
 
+    def generate_ftinfo(self, another_domain):
+        """
+        Generates TrustDomainInfoFullInfo2Internal structure
+        This structure allows to pass information about all domains associated
+        with the another domain's realm.
+
+        Only top level name and top level name exclusions are handled here. 
+        """
+        if not another_domain.ftinfo_records:
+            return
+
+        ftinfo_records = []
+        info = lsa.ForestTrustInformation()
+
+        for rec in another_domain.ftinfo_records:
+            record = lsa.ForestTrustRecord()
+            record.flags = 0
+            record.time = rec['rec_time']
+            record.type = rec['rec_type']
+            record.forest_trust_data.string = rec['rec_name']
+            ftinfo_records.append(record)
+
+        info.count = len(ftinfo_records)
+        info.entries = ftinfo_records
+        return info
+
+    def update_ftinfo(self, another_domain):
+        """
+        Updates forest trust information in this forest corresponding
+        to the another domain's information.
+        """
+        try:
+            if another_domain.ftinfo_records:
+                ftinfo = self.generate_ftinfo(another_domain)
+                # Set forest trust information -- we do it only against AD DC 
as
+                # smbd already has the information about itself
+                ldname = lsa.StringLarge()
+                ldname.string = another_domain.info['dns_domain']
+                collision_info = 
self._pipe.lsaRSetForestTrustInformation(self._policy_handle,
+                                                                          
ldname,
+                                                                          
lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
+                                                                          
ftinfo, 0)
+                if collision_info:
+                    root_logger.error("When setting forest trust information, 
got collision info back:\n%s" % (ndr_print(collision_info)))
+        except RuntimeError, e:
+            # We can ignore the error here -- setting up name suffix routes 
may fail
+            pass
 
     def establish_trust(self, another_domain, trustdom_secret):
         """
@@ -883,6 +931,12 @@ class TrustDomainInstance(object):
         Input: another_domain -- instance of TrustDomainInstance, initialized 
with #retrieve call
                trustdom_secret -- shared secred used for the trust
         """
+        if self.info['name'] == another_domain.info['name']:
+            # Check that NetBIOS names do not clash
+            raise errors.ValidationError(name=u'AD Trust Setup',
+                    error=_('the IPA server and the remote domain cannot share 
the same '
+                            'NetBIOS name: %s') % self.info['name'])
+
         self.generate_auth(trustdom_secret)
 
         info = lsa.TrustDomainInfoInfoEx()
@@ -893,12 +947,6 @@ class TrustDomainInstance(object):
         info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
         info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
 
-        if self.info['name'] == info.netbios_name.string:
-            # Check that NetBIOS names do not clash
-            raise errors.ValidationError(name=u'AD Trust Setup',
-                    error=_('the IPA server and the remote domain cannot share 
the same '
-                            'NetBIOS name: %s') % self.info['name'])
-
         try:
             dname = lsa.String()
             dname.string = another_domain.info['dns_domain']
@@ -911,12 +959,14 @@ class TrustDomainInstance(object):
         except RuntimeError, (num, message):
             raise assess_dcerpc_exception(num=num, message=message)
 
+        self.update_ftinfo(another_domain)
+
+        # We should use proper trustdom handle in order to modify the
+        # trust settings. Samba insists this has to be done with LSA
+        # OpenTrustedDomain* calls, it is not enough to have a handle
+        # returned by the CreateTrustedDomainEx2 call.
+        trustdom_handle = 
self._pipe.OpenTrustedDomainByName(self._policy_handle, dname, 
security.SEC_FLAG_MAXIMUM_ALLOWED)
         try:
-            # We should use proper trustdom handle in order to modify the
-            # trust settings. Samba insists this has to be done with LSA
-            # OpenTrustedDomain* calls, it is not enough to have a handle
-            # returned by the CreateTrustedDomainEx2 call.
-            trustdom_handle = 
self._pipe.OpenTrustedDomainByName(self._policy_handle, dname, 
security.SEC_FLAG_MAXIMUM_ALLOWED)
             infoclass = lsa.TrustDomainInfoSupportedEncTypes()
             infoclass.enc_types = security.KERB_ENCTYPE_RC4_HMAC_MD5
             infoclass.enc_types |= 
security.KERB_ENCTYPE_AES128_CTS_HMAC_SHA1_96
@@ -1016,6 +1066,32 @@ class TrustDomainJoins(object):
         # Otherwise, use anonymously obtained data
         self.remote_domain = rd
 
+    def get_realmdomains(self):
+        """
+        Generate list of records for forest trust information about
+        our realm domains. Note that the list generated currently
+        includes only top level domains, no exclusion domains, and no TDO 
objects
+        as we handle the latter in a separte way
+        """
+        if self.local_domain.read_only:
+            return
+
+       self.local_domain.ftinfo_records = []
+
+        realm_domains = self.api.Command.realmdomains_show()['result']
+        trustconfig = self.api.Command.trustconfig_show()['result']
+        # Use realmdomains' modification timestamp to judge records last 
update time
+        (dn, entry_attrs) = 
self.api.Backend.ldap2.get_entry(realm_domains['dn'], ['modifyTimestamp'])
+        # Convert the timestamp to Windows 64-bit timestamp format
+        trust_timestamp = 
long(time.mktime(time.strptime(entry_attrs['modifytimestamp'][0][:14], 
"%Y%m%d%H%M%S"))*1e7+116444736000000000)
+        
+        for dom in realm_domains['associateddomain']:
+            ftinfo = dict()
+            ftinfo['rec_name'] = dom
+            ftinfo['rec_time'] = trust_timestamp
+            ftinfo['rec_type'] = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME
+            self.local_domain.ftinfo_records.append(ftinfo)
+
     def join_ad_full_credentials(self, realm, realm_server, realm_admin, 
realm_passwd):
         if not self.configured:
             return None
@@ -1030,6 +1106,7 @@ class TrustDomainJoins(object):
 
         if not self.remote_domain.read_only:
             trustdom_pass = samba.generate_random_password(128, 128)
+            self.get_realmdomains()
             self.remote_domain.establish_trust(self.local_domain, 
trustdom_pass)
             self.local_domain.establish_trust(self.remote_domain, 
trustdom_pass)
             result = self.remote_domain.verify_trust(self.local_domain)
-- 
1.8.3.1

>From 2bec4dd4709a31d6dfbfc76c5b747ad4a290d742 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <aboko...@redhat.com>
Date: Wed, 18 Sep 2013 17:04:19 +0200
Subject: [PATCH 2/2] trusts: support subdomains in a forest

Add IPA CLI to manage trust domains.

ipa trustdomain-fetch <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 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                 |  72 ++++++++++++++
 ipalib/plugins/trust.py | 248 ++++++++++++++++++++++++++++++++++++++----------
 ipaserver/dcerpc.py     |  54 +++++++++++
 3 files changed, 323 insertions(+), 51 deletions(-)

diff --git a/API.txt b/API.txt
index 761d1d1..3239f53 100644
--- a/API.txt
+++ b/API.txt
@@ -3497,6 +3497,78 @@ 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_create
+args: 3,9,3
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+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: trustdomain_del
+args: 3,2,3
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+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: trustdomain_fetch
+args: 2,4,2
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+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: trustdomain_find
+args: 3,8,4
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+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: trustdomain_mod
+args: 3,10,3
+arg: Str('trustcn', cli_name='trust', query=True, required=True)
+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: 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 3c117b4..ed50952 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.
@@ -241,12 +248,24 @@ 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), filter(lambda x: x, keys))
+        sdn.reverse()
+        trust_type = kwargs.get('trust_type')
+        if not trust_type:
+            for ttype in self.trust_types:
+                dn=make_trust_dn(self.env, ttype, DN(*sdn))
+                try:
+                    object_exists = self.backend.get_entry(dn, [''])
+                    return object_exists.dn
+                except errors.NotFound:
+                    pass
+            # if no trust object of any type exist, default to 'ad'
+            # this is required for *_add calls.
+            trust_type = u'ad'
+
+        dn=make_trust_dn(self.env, trust_type, DN(*sdn))
+        return dn
 
 class trust_add(LDAPCreate):
     __doc__ = _('''
@@ -576,10 +595,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
@@ -652,14 +674,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).
@@ -673,16 +687,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.')
@@ -696,8 +704,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 = '(ipaNTSupportedEncryptionTypes=*)'
+        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):
@@ -705,7 +715,7 @@ class trust_find(LDAPSearch):
 
         for entry in entries:
             (dn, attrs) = entry
-
+            
             # Translate ipanttrusttype to trusttype if --raw not used
             if not options.get('raw', False):
                 attrs['trusttype'] = 
trust_type_string(attrs['ipanttrusttype'][0])
@@ -718,30 +728,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
@@ -1078,3 +1064,163 @@ 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')
+
+    trustdomain_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'),
+        ),
+    )
+api.register(trustdomain)
+
+class trustdomain_find(LDAPSearch):
+    __doc__ = _('Search domains of the trust')
+
+    takes_args = trustdomain.trustdomain_args[0]
+    takes_options = LDAPSearch.takes_options + 
(trustdomain.trustdomain_args[1],)
+
+    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_args = trustdomain.trustdomain_args
+    takes_options = LDAPUpdate.takes_options + (_trust_type_option,)
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, 
**options):
+        args = filter(lambda x: x, keys)
+        if len(args) != 2:
+            raise errors.ValidationError(name=_('AD trust'), error=_('Trust 
name and trust domain name are required to modify the trust domain'))
+        return dn
+api.register(trustdomain_mod)
+
+class trustdomain_add(LDAPCreate):
+    __doc__ = _('Allow access from the trusted domain')
+    NO_CLI = True
+
+    takes_args = trustdomain.trustdomain_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):
+        args = filter(lambda x: x, keys)
+        if len(args) != 2:
+            raise errors.ValidationError(name=_('AD trust'), error=_('Trust 
name and trust domain name are required to create the trust domain'))
+        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"')
+
+    takes_args = trustdomain.trustdomain_args
+    def pre_callback(self, ldap, dn, *keys, **options):
+        args = filter(lambda x: x, keys)
+        if len(args) != 2:
+            raise errors.ValidationError(name=_('AD trust'), error=_('Trust 
name and trust domain name are required to delete the trust domain'))
+        return dn
+
+    def execute(self, *keys, **options):
+        result = super(trustdomain_del, self).execute(*keys, **options)
+        result['value'] = keys[1]
+        return result
+
+
+api.register(trustdomain_del)
+
+class trustdomain_fetch(LDAPRetrieve):
+    __doc__ = _('Refresh list of the domains associated with the trust')
+
+    takes_args = (trustdomain.trustdomain_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 not keys[0]:
+            raise errors.ValidationError(name=_('AD trust'), error=_('Trust 
name is required to fetch trust domains'))
+
+        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 trust domains were detected 
during refresh'))
+            return result
+
+        for dom in domains:
+            dom['trust_type'] = 
self.obj.trust_type_idx[trust['ipanttrusttype'][0]]
+            try:
+                name = dom['cn']
+                del dom['cn']
+                res = self.api.Command.trustdomain_add(keys[0], name, **dom)
+                result['result'].append(res['result'])
+            except errors.DuplicateEntry:
+                # Ignore updating duplicate entries
+                pass
+        if len(result['result']) > 0:
+            result['summary'] = unicode(_('List of trust domains successfully 
refreshed'))
+        else:
+            result['summary'] = unicode(_('No new trust domains were found'))
+        return result
+api.register(trustdomain_fetch)
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index c24230b..af0e77e 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

_______________________________________________
Freeipa-devel mailing list
Freeipa-devel@redhat.com
https://www.redhat.com/mailman/listinfo/freeipa-devel

Reply via email to