mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
ipaserver/dcerpc: store forest topology as a blob in ipasam
winbindd expects forest topology information blob to find out child domains. We should store it in LDAP and let ipasam to retrieve it. In fact, ipasam already supports updating and loading this information but during 'ipa trust-fetch-domains' we didn't provide it. Make sure the blob is preserved after it was retrieved and also updated when we fetch forest topology information. Fixes: https://pagure.io/freeipa/issue/8576 Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com> Reviewed-By: Christian Heimes <cheimes@redhat.com> Reviewed-By: Rob Crittenden <rcritten@redhat.com>
This commit is contained in:
parent
08d7d90ab0
commit
214aeb7243
@ -24,6 +24,7 @@
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from contextlib import contextmanager
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
@ -852,6 +853,7 @@ class TrustDomainInstance:
|
||||
self._policy_handle = None
|
||||
self.read_only = False
|
||||
self.ftinfo_records = None
|
||||
self.ftinfo_data = None
|
||||
self.validation_attempts = 0
|
||||
|
||||
def __gen_lsa_connection(self, binding):
|
||||
@ -1011,6 +1013,16 @@ class TrustDomainInstance:
|
||||
|
||||
self.info['is_pdc'] = (result.role == lsa.LSA_ROLE_PRIMARY)
|
||||
|
||||
if all([self.info['is_pdc'],
|
||||
self.info['dns_domain'] == self.info['dns_forest']]):
|
||||
try:
|
||||
netr_pipe = netlogon.netlogon(self.binding,
|
||||
self.parm, self.creds)
|
||||
self.ftinfo_data = netr_pipe.netr_DsRGetForestTrustInformation(
|
||||
self.info['dc'], None, 0)
|
||||
except RuntimeError as e:
|
||||
raise assess_dcerpc_error(e)
|
||||
|
||||
def generate_auth(self, trustdom_secret):
|
||||
password_blob = string_to_array(trustdom_secret.encode('utf-16-le'))
|
||||
|
||||
@ -1067,6 +1079,9 @@ class TrustDomainInstance:
|
||||
|
||||
Only top level name and top level name exclusions are handled here.
|
||||
"""
|
||||
if another_domain.ftinfo_data is not None:
|
||||
return another_domain.ftinfo_data
|
||||
|
||||
if not another_domain.ftinfo_records:
|
||||
return None
|
||||
|
||||
@ -1501,21 +1516,14 @@ class TrustDomainInstance:
|
||||
return False
|
||||
|
||||
|
||||
def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
def communicate(td):
|
||||
td.init_lsa_pipe(td.info['dc'])
|
||||
netr_pipe = netlogon.netlogon(td.binding, td.parm, td.creds)
|
||||
# Older IPA versions used netr_DsrEnumerateDomainTrusts call
|
||||
# but it doesn't provide information about non-domain UPNs associated
|
||||
# with the forest, thus we have to use netr_DsRGetForestTrustInformation
|
||||
domains = netr_pipe.netr_DsRGetForestTrustInformation(td.info['dc'], None, 0)
|
||||
return domains
|
||||
|
||||
domains = None
|
||||
@contextmanager
|
||||
def discover_trust_instance(api, mydomain, trustdomain,
|
||||
creds=None, server=None):
|
||||
domain_validator = DomainValidator(api)
|
||||
configured = domain_validator.is_configured()
|
||||
if not configured:
|
||||
return None
|
||||
yield None
|
||||
return
|
||||
|
||||
td = TrustDomainInstance('')
|
||||
td.parm.set('workgroup', mydomain)
|
||||
@ -1541,9 +1549,11 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
# Rely on existing Kerberos credentials in the environment
|
||||
td.creds = credentials.Credentials()
|
||||
td.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS)
|
||||
enforce_smb_encryption(td.creds)
|
||||
td.creds.guess(td.parm)
|
||||
td.creds.set_workstation(domain_validator.flatname)
|
||||
domains = communicate(td)
|
||||
logger.error('environment: %s', str(os.environ))
|
||||
yield td
|
||||
else:
|
||||
# Attempt to authenticate as HTTP/ipa.master and use cross-forest trust
|
||||
# or as passed-in user in case of a one-way trust
|
||||
@ -1559,10 +1569,33 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
'cross-forest communication'))
|
||||
td.creds = credentials.Credentials()
|
||||
td.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS)
|
||||
enforce_smb_encryption(td.creds)
|
||||
if ccache_name:
|
||||
with ipautil.private_ccache(path=ccache_name):
|
||||
td.creds.guess(td.parm)
|
||||
td.creds.set_workstation(domain_validator.flatname)
|
||||
yield td
|
||||
|
||||
|
||||
def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
def communicate(td):
|
||||
td.init_lsa_pipe(td.info['dc'])
|
||||
netr_pipe = netlogon.netlogon(td.binding, td.parm, td.creds)
|
||||
# Older FreeIPA versions used netr_DsrEnumerateDomainTrusts call
|
||||
# but it doesn't provide information about non-domain UPNs associated
|
||||
# with the forest, thus we have to use netr_DsRGetForestTrustInformation
|
||||
domains = netr_pipe.netr_DsRGetForestTrustInformation(td.info['dc'],
|
||||
None, 0)
|
||||
return domains
|
||||
|
||||
domains = None
|
||||
with discover_trust_instance(api, mydomain, trustdomain,
|
||||
creds=creds, server=server) as td:
|
||||
if td is None:
|
||||
return None
|
||||
if td.ftinfo_data is not None:
|
||||
domains = td.ftinfo_data
|
||||
else:
|
||||
domains = communicate(td)
|
||||
|
||||
if domains is None:
|
||||
@ -1574,11 +1607,23 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
# top level name info -- a name suffix associated with the forest
|
||||
# We should ignore forest root name/name suffix as it is already part
|
||||
# of trust information for IPA purposes and only add what's inside the forest
|
||||
ftinfo_records = []
|
||||
ftinfo = drsblobs.ForestTrustInfo()
|
||||
for t in domains.entries:
|
||||
record = drsblobs.ForestTrustInfoRecord()
|
||||
record.flags = t.flags
|
||||
record.timestamp = t.time
|
||||
record.type = t.type
|
||||
|
||||
if t.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
|
||||
record.data.sid = t.forest_trust_data.domain_sid
|
||||
record.data.dns_name.string = \
|
||||
t.forest_trust_data.dns_domain_name.string
|
||||
record.data.netbios_name.string = \
|
||||
t.forest_trust_data.netbios_domain_name.string
|
||||
|
||||
tname = unicode(t.forest_trust_data.dns_domain_name.string)
|
||||
if tname == trustdomain:
|
||||
continue
|
||||
if tname != trustdomain:
|
||||
result['domains'][tname] = {
|
||||
'cn': tname,
|
||||
'ipantflatname': unicode(
|
||||
@ -1587,11 +1632,23 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
t.forest_trust_data.domain_sid)
|
||||
}
|
||||
elif t.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
|
||||
record.data.string = t.forest_trust_data.string
|
||||
|
||||
tname = unicode(t.forest_trust_data.string)
|
||||
if tname == trustdomain:
|
||||
continue
|
||||
|
||||
result['suffixes'][tname] = {'cn': tname}
|
||||
elif t.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
|
||||
record.data.string = t.forest_trust_data.string
|
||||
|
||||
rc = drsblobs.ForestTrustInfoRecordArmor()
|
||||
rc.record = record
|
||||
ftinfo_records.append(rc)
|
||||
|
||||
ftinfo.count = len(ftinfo_records)
|
||||
ftinfo.records = ftinfo_records
|
||||
result['ftinfo_data'] = ndr_pack(ftinfo)
|
||||
return result
|
||||
|
||||
|
||||
@ -1785,10 +1842,15 @@ class TrustDomainJoins:
|
||||
trustdom_pass,
|
||||
trust_type, trust_external)
|
||||
|
||||
# For local domain we don't set topology information
|
||||
try:
|
||||
self.local_domain.establish_trust(self.remote_domain,
|
||||
trustdom_pass,
|
||||
trust_type, trust_external)
|
||||
except TrustTopologyConflictSolved:
|
||||
self.local_domain.establish_trust(self.remote_domain,
|
||||
trustdom_pass,
|
||||
trust_type, trust_external)
|
||||
|
||||
# if trust is inbound, we don't need to verify it because
|
||||
# AD DC will respond with WERR_NO_SUCH_DOMAIN --
|
||||
# it only does verification for outbound trusts.
|
||||
|
@ -1744,6 +1744,9 @@ def add_new_domains_from_trust(myapi, trustinstance, trust_entry,
|
||||
tlns = entry.get('ipantadditionalsuffixes', [])
|
||||
tlns.extend(x for x in suffixes if x not in tlns)
|
||||
entry['ipantadditionalsuffixes'] = tlns
|
||||
ftidata = domains.get('ftinfo_data', None)
|
||||
if ftidata is not None:
|
||||
entry['ipanttrustforesttrustinfo'] = [ftidata]
|
||||
ldap.update_entry(entry)
|
||||
except errors.EmptyModlist:
|
||||
pass
|
||||
|
Loading…
Reference in New Issue
Block a user