martbab's pull request #46: "Always fetch forest info from root DCs when 
establishing two-way trust" was synchronize

See the full pull-request at https://github.com/freeipa/freeipa/pull/46
... or pull the PR as Git branch:
git remote add ghfreeipa https://github.com/freeipa/freeipa
git fetch ghfreeipa pull/46/head:pr46
git checkout pr46
From 5a70f5dc53067f7a21a4fc60f95d7b11b2220611 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Thu, 1 Sep 2016 09:30:23 +0200
Subject: [PATCH 1/3] Always fetch forest info from root DCs when establishing
 two-way trust

Prior To Windows Server 2012R2, the `netr_DsRGetForestTrustInformation` calls
performed against non-root forest domain DCs were automatically routed to the
root domain DCs to resolve trust topology information.

This is no longer the case, so the `dcerpc.fetch_domains` function must
explicitly contact root domain DCs even in the case when an external two-way
trust to non-root domain is requested.

https://fedorahosted.org/freeipa/ticket/6057
---
 ipaserver/plugins/trust.py | 29 +++++++++++++++++++++--------
 1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py
index 65dc1f4..8f8f987 100644
--- a/ipaserver/plugins/trust.py
+++ b/ipaserver/plugins/trust.py
@@ -770,7 +770,7 @@ def execute(self, *keys, **options):
                 # Bidirectional trust allows us to use cross-realm TGT, so we can
                 # run the call under original user's credentials
                 res = fetch_domains_from_trust(self.api, self.trustinstance,
-                                               result['result'], **options)
+                                               **options)
                 domains = add_new_domains_from_trust(self.api, self.trustinstance,
                                                      result['result'], res, **options)
             else:
@@ -1631,8 +1631,21 @@ def execute(self, *keys, **options):
         return result
 
 
-def fetch_domains_from_trust(myapi, trustinstance, trust_entry, **options):
-    trust_name = trust_entry['cn'][0]
+def fetch_domains_from_trust(myapi, trustinstance, **options):
+    """
+    Contact trust forest root DC and fetch trusted forest topology information.
+
+    :param myapi: API instance
+    :param trustinstance: Initialized instance of `dcerpc.TrustDomainJoins`
+        class
+    :param options: options passed from API command's `execute()` method
+
+    :returns: dict containing forest domain information and forest-wide UPN
+        suffixes (if any)
+    """
+
+    forest_root_name = trustinstance.remote_domain.info['dns_forest']
+
     # We want to use Kerberos if we have admin credentials even with SMB calls
     # as eventually use of NTLMSSP will be deprecated for trusted domain operations
     # If admin credentials are missing, 'creds' will be None and fetch_domains
@@ -1640,10 +1653,10 @@ def fetch_domains_from_trust(myapi, trustinstance, trust_entry, **options):
     # as well.
     creds = generate_creds(trustinstance, style=CRED_STYLE_KERBEROS, **options)
     server = options.get('realm_server', None)
-    domains = ipaserver.dcerpc.fetch_domains(myapi,
-                                             trustinstance.local_flatname,
-                                             trust_name, creds=creds,
-                                             server=server)
+    domains = ipaserver.dcerpc.fetch_domains(
+        myapi, trustinstance.local_flatname, forest_root_name, creds=creds,
+        server=server)
+
     return domains
 
 
@@ -1749,7 +1762,7 @@ def execute(self, *keys, **options):
                     'on the IPA server first'
                 )
             )
-        res = fetch_domains_from_trust(self.api, trustinstance, trust, **options)
+        res = fetch_domains_from_trust(self.api, trustinstance, **options)
         domains = add_new_domains_from_trust(self.api, trustinstance, trust, res, **options)
 
         if len(domains) > 0:

From 11e3bca0af0ff8969b2eddb9e0b19fcf6a4a9fd0 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Thu, 1 Sep 2016 18:09:05 +0200
Subject: [PATCH 2/3] factor out `populate_remote_domain` method into
 module-level function

This allows for re-use of this method in cases where the caller can not or
wishes not to instantiate local Samba domain to retrieve information about
remote ones.

https://fedorahosted.org/freeipa/ticket/6057
---
 ipaserver/dcerpc.py | 94 ++++++++++++++++++++++++++++++-----------------------
 1 file changed, 53 insertions(+), 41 deletions(-)

diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 4d98485..71b8ba6 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -1534,6 +1534,52 @@ def communicate(td):
     return result
 
 
+def retrieve_remote_domain(hostname, local_flatname,
+                           realm, realm_server=None,
+                           realm_admin=None, realm_passwd=None):
+    def get_instance(local_flatname):
+        # Fetch data from foreign domain using password only
+        rd = TrustDomainInstance('')
+        rd.parm.set('workgroup', local_flatname)
+        rd.creds = credentials.Credentials()
+        rd.creds.set_kerberos_state(credentials.DONT_USE_KERBEROS)
+        rd.creds.guess(rd.parm)
+        return rd
+
+    rd = get_instance(local_flatname)
+    rd.creds.set_anonymous()
+    rd.creds.set_workstation(hostname)
+    if realm_server is None:
+        rd.retrieve_anonymously(realm, discover_srv=True, search_pdc=True)
+    else:
+        rd.retrieve_anonymously(realm_server,
+                                discover_srv=False, search_pdc=True)
+    rd.read_only = True
+    if realm_admin and realm_passwd:
+        if 'name' in rd.info:
+            names = realm_admin.split('\\')
+            if len(names) > 1:
+                # realm admin is in DOMAIN\user format
+                # strip DOMAIN part as we'll enforce the one discovered
+                realm_admin = names[-1]
+            auth_string = u"%s\%s%%%s" \
+                          % (rd.info['name'], realm_admin, realm_passwd)
+            td = get_instance(local_flatname)
+            td.creds.parse_string(auth_string)
+            td.creds.set_workstation(hostname)
+            if realm_server is None:
+                # we must have rd.info['dns_hostname'] then
+                # as it is part of the anonymous discovery
+                td.retrieve(rd.info['dns_hostname'])
+            else:
+                td.retrieve(realm_server)
+            td.read_only = False
+            return td
+
+    # Otherwise, use anonymously obtained data
+    return rd
+
+
 class TrustDomainJoins(object):
     def __init__(self, api):
         self.api = api
@@ -1565,47 +1611,13 @@ def __populate_local_domain(self):
 
     def populate_remote_domain(self, realm, realm_server=None,
                                realm_admin=None, realm_passwd=None):
-        def get_instance(self):
-            # Fetch data from foreign domain using password only
-            rd = TrustDomainInstance('')
-            rd.parm.set('workgroup', self.local_domain.info['name'])
-            rd.creds = credentials.Credentials()
-            rd.creds.set_kerberos_state(credentials.DONT_USE_KERBEROS)
-            rd.creds.guess(rd.parm)
-            return rd
-
-        rd = get_instance(self)
-        rd.creds.set_anonymous()
-        rd.creds.set_workstation(self.local_domain.hostname)
-        if realm_server is None:
-            rd.retrieve_anonymously(realm, discover_srv=True, search_pdc=True)
-        else:
-            rd.retrieve_anonymously(realm_server,
-                                    discover_srv=False, search_pdc=True)
-        rd.read_only = True
-        if realm_admin and realm_passwd:
-            if 'name' in rd.info:
-                names = realm_admin.split('\\')
-                if len(names) > 1:
-                    # realm admin is in DOMAIN\user format
-                    # strip DOMAIN part as we'll enforce the one discovered
-                    realm_admin = names[-1]
-                auth_string = u"%s\%s%%%s" \
-                              % (rd.info['name'], realm_admin, realm_passwd)
-                td = get_instance(self)
-                td.creds.parse_string(auth_string)
-                td.creds.set_workstation(self.local_domain.hostname)
-                if realm_server is None:
-                    # we must have rd.info['dns_hostname'] then
-                    # as it is part of the anonymous discovery
-                    td.retrieve(rd.info['dns_hostname'])
-                else:
-                    td.retrieve(realm_server)
-                td.read_only = False
-                self.remote_domain = td
-                return
-        # Otherwise, use anonymously obtained data
-        self.remote_domain = rd
+        self.remote_domain = retrieve_remote_domain(
+            self.local_domain.hostname,
+            self.local_domain.info['name'],
+            realm,
+            realm_server=realm_server,
+            realm_admin=realm_admin,
+            realm_passwd=realm_passwd)
 
     def get_realmdomains(self):
         """

From 4f93d0422a5dad0e49d71dd127ed65ffac360fa2 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Thu, 1 Sep 2016 18:14:22 +0200
Subject: [PATCH 3/3] Always fetch forest info from root DCs when establishing
 one-way trust

Prior To Windows Server 2012R2, the `netr_DsRGetForestTrustInformation` calls
performed against non-root forest domain DCs were automatically routed to
the root domain DCs to resolve trust topology information.

This is no longer the case, so the `com.redhat.idm.trust-fetch-domains` oddjob
helper used to establish one-way needs to explicitly contact root domain DCs
even in the case when an external trust to non-root domain is requested.

https://fedorahosted.org/freeipa/ticket/6057
---
 install/oddjob/com.redhat.idm.trust-fetch-domains | 25 ++++++++++++++++++++++-
 1 file changed, 24 insertions(+), 1 deletion(-)

diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains
index bffa021..b4b3e8f 100755
--- a/install/oddjob/com.redhat.idm.trust-fetch-domains
+++ b/install/oddjob/com.redhat.idm.trust-fetch-domains
@@ -40,6 +40,24 @@ def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
         pass
 
 
+def get_forest_root_domain(api_instance, trusted_domain):
+    """
+    retrieve trusted forest root domain for given domain name
+
+    :param api_instance: IPA API instance
+    :param trusted_domain: trusted domain name
+
+    :returns: forest root domain DNS name
+    """
+    trustconfig_show = api_instance.Command.trustconfig_show
+    flatname = trustconfig_show()['result']['ipantflatname'][0]
+
+    remote_domain = dcerpc.retrieve_remote_domain(
+        api.env.host, flatname, trusted_domain)
+
+    return remote_domain.info['dns_forest']
+
+
 def parse_options():
     usage = "%prog <trusted domain name>\n"
     parser = config.IPAOptionParser(usage=usage,
@@ -169,7 +187,12 @@ except gssapi.exceptions.GSSError:
 # We are done: we have ccache with TDO credentials and can fetch domains
 ipa_domain = api.env.domain
 os.environ['KRB5CCNAME'] = oneway_ccache_name
-domains = dcerpc.fetch_domains(api, ipa_domain, trusted_domain, creds=True)
+
+# retrieve the forest root domain name and contact it to retrieve trust
+# topology info
+forest_root = get_forest_root_domain(api, trusted_domain)
+
+domains = dcerpc.fetch_domains(api, ipa_domain, forest_root, creds=True)
 trust_domain_object = api.Command.trust_show(trusted_domain, raw=True)['result']
 trust.add_new_domains_from_trust(api, None, trust_domain_object, domains)
 
-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to