ipaserver/dcerpc: fix exclusion entry with a forest trust domain info returned

When looking through the topology of a trusted forest, we should support
all types of forest trust records. Since Samba Python bindings parse the
data into a typed structure, a type of the record has to be taken into
account or there will be type mismatch when accessing elements of the
union:

        typedef [switch_type(lsa_ForestTrustRecordType)] union {
                [case(LSA_FOREST_TRUST_TOP_LEVEL_NAME)] lsa_StringLarge top_level_name;
                [case(LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX)] lsa_StringLarge top_level_name_ex;
                [case(LSA_FOREST_TRUST_DOMAIN_INFO)] lsa_ForestTrustDomainInfo domain_info;
                [default] lsa_ForestTrustBinaryData data;
        } lsa_ForestTrustData;

        typedef struct {
                lsa_ForestTrustRecordFlags flags;
                lsa_ForestTrustRecordType type;
                NTTIME_hyper time;
                [switch_is(type)] lsa_ForestTrustData forest_trust_data;
        } lsa_ForestTrustRecord;

        typedef [public] struct {
                [range(0,4000)] uint32 count;
                [size_is(count)] lsa_ForestTrustRecord **entries;
        } lsa_ForestTrustInformation;

Each entry in the lsa_ForestTrustInformation has forest_trust_data
member but its content depends on the value of a type member
(forest_trust_data is a union of all possible structures).

Previously we assumed only TLN or TLN exclusion record which were
of the same type (lsa_StringLarge). Access to forest_trust_data.string
fails when forest_trust_data's type is lsa_ForestTrustDomainInfo as it
has no string member.

Fix the code by properly accessing the dns_domain_name from the
lsa_ForestTrustDomainInfo structure.

Fixes: https://pagure.io/freeipa/issue/7828
Reviewed-By: Christian Heimes <cheimes@redhat.com>
This commit is contained in:
Alexander Bokovoy 2019-01-07 15:28:29 +02:00 committed by Christian Heimes
parent 3bed3d4ddb
commit 3c38aea6fc

View File

@ -51,6 +51,7 @@ from samba.dcerpc import security, lsa, drsblobs, nbt, netlogon
from samba.ndr import ndr_pack, ndr_print
from samba import net
from samba import arcfour_encrypt
from samba import ntstatus
import samba
import ldap as _ldap
@ -1105,6 +1106,25 @@ class TrustDomainInstance:
original forest.
"""
def domain_name_from_ftinfo(ftinfo):
"""
Returns a domain name string from a ForestTrustRecord
:param ftinfo: LSA ForestTrustRecord to parse
"""
if ftinfo.type == lsa.LSA_FOREST_TRUST_DOMAIN_INFO:
return ftinfo.forest_trust_data.dns_domain_name.string
elif ftinfo.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME:
return ftinfo.forest_trust_data.string
elif ftinfo.type == lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX:
# We should ignore TLN exclusion record because it
# is already an exclusion so we aren't going to
# change anything here
return None
else:
# Ignore binary blobs we don't know about
return None
# List of entries for unsolved conflicts
result = []
@ -1145,18 +1165,26 @@ class TrustDomainInstance:
e1.time = e.time
e1.forest_trust_data = e.forest_trust_data
# We either have a domain struct, a TLN name,
# or a TLN exclusion name in the list.
# The rest we should skip, those are binary blobs
dns_domain_name = domain_name_from_ftinfo(e)
# Search for a match in the topology of another domain
# if there is a match, we have to convert a record
# into a TLN exclusion to allow its routing to the
# another domain
for r in another_domain.ftinfo_records:
if r['rec_name'] == e.forest_trust_data.string:
# r['rec_name'] cannot be None, thus we can ignore
# the case when dns_domain_name is None
if r['rec_name'] == dns_domain_name:
is_our_record = True
# Convert e1 into an exclusion record
e1.type = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME_EX
e1.flags = 0
e1.time = trust_timestamp
e1.forest_trust_data.string = dns_domain_name
break
entries.append(e1)
@ -1180,11 +1208,29 @@ class TrustDomainInstance:
# Update the forest trust information now
ldname = lsa.StringLarge()
ldname.string = rec.name.string
cninfo = self._pipe.lsaRSetForestTrustInformation(
self._policy_handle,
ldname,
lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
fti, 0)
cninfo = None
try:
cninfo = self._pipe.lsaRSetForestTrustInformation(
self._policy_handle,
ldname,
lsa.LSA_FOREST_TRUST_DOMAIN_INFO,
fti, 0)
except samba.NTSTATUSError as error:
# Handle NT_STATUS_INVALID_PARAMETER separately
if ntstatus.NT_STATUS_INVALID_PARAMETER == error.args[0]:
result.append(rec)
logger.error("Unable to resolve conflict for "
"DNS domain %s in the forest %s "
"for in-forest domain %s. Trust cannot "
"be established unless this conflict "
"is fixed manually.",
another_domain.info['dns_domain'],
self.info['dns_domain'],
rec.name.string)
else:
raise assess_dcerpc_error(error)
if cninfo:
result.append(rec)
logger.error("When defining exception for DNS "
@ -1213,9 +1259,9 @@ class TrustDomainInstance:
# Otherwise, raise TrustTopologyConflictError() exception
domains = [x.name.string for x in result]
raise errors.TrustTopologyConflictError(
target=self.info['dns_domain'],
conflict=another_domain.info['dns_domain'],
domains=domains)
forest=self.info['dns_domain'],
conflict=another_domain.info['dns_domain'],
domains=domains)