From e5471e66c6a718ffa28433813b8a8d7896b16d9e Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Mon, 7 Jan 2019 15:28:29 +0200
Subject: [PATCH] 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>
---
ipaserver/dcerpc.py | 64 ++++++++++++++++++++++++++++++++++++++-------
1 file changed, 55 insertions(+), 9 deletions(-)
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 125493657..51a8e82e7 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -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(object):
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(object):
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(object):
# 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(object):
# 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)
--
2.20.1