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