[Freeipa-devel] [PATCH] 0154-0158 improve trust operations

Alexander Bokovoy abokovoy at redhat.com
Thu Aug 21 10:43:35 UTC 2014


Hi!

Attached patchset improves trust operations:

1. Ensures we only allow establishing trust to forest root domain
2. Ensures that we select primary domain controllers
3. Ensures first create trust and later set it to transitive state and
   update forest topology
4. Relaxes filtering of domains obtained from AD side to allow some of
   possible topology combinations which were not accounted for
   previously
5. Reverts to any PDC rather than a closest one if closest one is not
   available due to site mismanagement.

Affected tickets:
  https://fedorahosted.org/freeipa/ticket/4463
  https://fedorahosted.org/freeipa/ticket/4479
  https://fedorahosted.org/freeipa/ticket/4458

The patches should apply cleanly to master and ipa-3-3 (and 4-0/4-1
branches).

They were tested with Windows Server 2008R2 and Windows Server 2012
environments.

-- 
/ Alexander Bokovoy
-------------- next part --------------
From 18b27e8363799070cce57ab393787c99fa7ebc77 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 19 Aug 2014 16:19:45 +0300
Subject: [PATCH 1/5] ipaserver/dcerpc.py: if search of a closest GC failed, 
 try to find any GC

https://fedorahosted.org/freeipa/ticket/4458
---
 ipaserver/dcerpc.py | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index f1c7508..b11476a 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -588,7 +588,11 @@ class DomainValidator(object):
         try:
             result = netrc.finddc(domain=domain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_GC | nbt.NBT_SERVER_CLOSEST)
         except RuntimeError, e:
-            finddc_error = e
+            try:
+                # If search of closest GC failed, attempt to find any one
+                result = netrc.finddc(domain=domain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_GC)
+            except RuntimeError, e:
+                finddc_error = e
 
         if not self._domains:
             self._domains = self.get_trusted_domains()
-- 
1.9.3

-------------- next part --------------
From 96e5022a65798f4f4961ea904ce639ffe4477dc1 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 19 Aug 2014 16:21:21 +0300
Subject: [PATCH 2/5] ipaserver/dcerpc.py: make PDC discovery more robust

Certain operations against AD domain controller can only be done if its
FSMO role is primary domain controller. We need to use writable DC and
PDC when creating trust and updating name suffix routing information.

https://fedorahosted.org/freeipa/ticket/4479
---
 ipaserver/dcerpc.py | 21 ++++++++++++++++-----
 1 file changed, 16 insertions(+), 5 deletions(-)

diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index b11476a..78bfc5d 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -706,16 +706,19 @@ class TrustDomainInstance(object):
         binding_template=lambda x,y,z: u'%s:%s[%s]' % (x, y, z)
         return [binding_template(t, remote_host, o) for t in transports for o in options]
 
-    def retrieve_anonymously(self, remote_host, discover_srv=False):
+    def retrieve_anonymously(self, remote_host, discover_srv=False, search_pdc=False):
         """
         When retrieving DC information anonymously, we can't get SID of the domain
         """
         netrc = net.Net(creds=self.creds, lp=self.parm)
+        flags = nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS | nbt.NBT_SERVER_WRITABLE
+        if search_pdc:
+            flags = flags | nbt.NBT_SERVER_PDC
         try:
             if discover_srv:
-                result = netrc.finddc(domain=remote_host, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
+                result = netrc.finddc(domain=remote_host, flags=flags)
             else:
-                result = netrc.finddc(address=remote_host, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
+                result = netrc.finddc(address=remote_host, flags=flags)
         except RuntimeError, e:
             raise assess_dcerpc_exception(message=str(e))
 
@@ -726,6 +729,7 @@ class TrustDomainInstance(object):
         self.info['dns_forest'] = unicode(result.forest)
         self.info['guid'] = unicode(result.domain_uuid)
         self.info['dc'] = unicode(result.pdc_dns_name)
+        self.info['is_pdc'] = (result.server_type & nbt.NBT_SERVER_PDC) != 0
 
         # Netlogon response doesn't contain SID of the domain.
         # We need to do rootDSE search with LDAP_SERVER_EXTENDED_DN_OID control to reveal the SID
@@ -774,6 +778,13 @@ class TrustDomainInstance(object):
         self.info['sid'] = unicode(result.sid)
         self.info['dc'] = remote_host
 
+        try:
+            result = self._pipe.QueryInfoPolicy2(self._policy_handle, lsa.LSA_POLICY_INFO_ROLE)
+        except RuntimeError, (num, message):
+            raise assess_dcerpc_exception(num=num, message=message)
+
+        self.info['is_pdc'] = (result.role == lsa.LSA_ROLE_PRIMARY)
+
     def generate_auth(self, trustdom_secret):
         def arcfour_encrypt(key, data):
             c = RC4.RC4(key)
@@ -1069,9 +1080,9 @@ class TrustDomainJoins(object):
         rd.creds.set_anonymous()
         rd.creds.set_workstation(self.local_domain.hostname)
         if realm_server is None:
-            rd.retrieve_anonymously(realm, discover_srv=True)
+            rd.retrieve_anonymously(realm, discover_srv=True, search_pdc=True)
         else:
-            rd.retrieve_anonymously(realm_server, discover_srv=False)
+            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:
-- 
1.9.3

-------------- next part --------------
From 29248c0cfcf7885f66b0d001bcbe693b3eb285f0 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 19 Aug 2014 16:22:54 +0300
Subject: [PATCH 3/5] ipaserver/dcerpc.py: Avoid hitting issue with transitive 
 trusts on Windows Server prior to 2012

http://msdn.microsoft.com/en-us/library/2a769a08-e023-459f-aebe-4fb3f595c0b7#id83
---
 ipaserver/dcerpc.py | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 78bfc5d..3adac8b 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -900,7 +900,7 @@ class TrustDomainInstance(object):
         info.sid = security.dom_sid(another_domain.info['sid'])
         info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
         info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
-        info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
+        info.trust_attributes = 0
 
         try:
             dname = lsa.String()
@@ -917,8 +917,6 @@ class TrustDomainInstance(object):
         except RuntimeError, (num, message):
             raise assess_dcerpc_exception(num=num, message=message)
 
-        self.update_ftinfo(another_domain)
-
         # We should use proper trustdom handle in order to modify the
         # trust settings. Samba insists this has to be done with LSA
         # OpenTrustedDomain* calls, it is not enough to have a handle
@@ -937,6 +935,15 @@ class TrustDomainInstance(object):
             # server as that one doesn't support AES encryption types
             pass
 
+        try:
+            info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
+            self._pipe.SetInformationTrustedDomain(trustdom_handle, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX, info)
+        except RuntimeError, e:
+            root_logger.error('unable to set trust to transitive: %s' % (str(e)))
+            pass
+        if self.info['is_pdc']:
+            self.update_ftinfo(another_domain)
+
     def verify_trust(self, another_domain):
         def retrieve_netlogon_info_2(domain, function_code, data):
             try:
-- 
1.9.3

-------------- next part --------------
From 33521446e4344d5f7c80d70ed5907f75fcce2b65 Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 19 Aug 2014 16:23:58 +0300
Subject: [PATCH 4/5] ipaserver/dcerpc.py: be more open to what domains can be 
 seen through the forest trust

https://fedorahosted.org/freeipa/ticket/4463
---
 ipaserver/dcerpc.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 3adac8b..118b657 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -1038,7 +1038,7 @@ def fetch_domains(api, mydomain, trustdomain, creds=None):
 
     result = []
     for t in domains.array:
-        if ((t.trust_attributes & trust_attributes['NETR_TRUST_ATTRIBUTE_WITHIN_FOREST']) and
+        if (not (t.trust_flags & trust_flags['NETR_TRUST_FLAG_PRIMARY']) and
             (t.trust_flags & trust_flags['NETR_TRUST_FLAG_IN_FOREST'])):
             res = dict()
             res['cn'] = unicode(t.dns_name)
-- 
1.9.3

-------------- next part --------------
From 64b6d18522bb3b9c4bb61075d36362ba7b6e24ca Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Tue, 19 Aug 2014 16:24:27 +0300
Subject: [PATCH 5/5] ipaserver/dcerpc.py: Make sure trust is established only
 to forest root domain

Part of https://fedorahosted.org/freeipa/ticket/4463
---
 ipalib/errors.py    | 16 ++++++++++++++++
 ipaserver/dcerpc.py |  6 ++++++
 2 files changed, 22 insertions(+)

diff --git a/ipalib/errors.py b/ipalib/errors.py
index 716decb..405c5c3 100644
--- a/ipalib/errors.py
+++ b/ipalib/errors.py
@@ -810,6 +810,22 @@ class DeprecationError(InvocationError):
     errno = 3015
     format = _("Command '%(name)s' has been deprecated")
 
+class NotAForestRootError(InvocationError):
+    """
+    **3016** Raised when an attempt to establish trust is done against non-root domain
+             Forest root domain has the same name as the forest itself
+
+    For example:
+
+    >>> raise NotAForestRootError(forest='example.test', domain='jointops.test')
+    Traceback (most recent call last):
+      ...
+    NotAForestRootError: Domain 'jointops.test' is not a root domain for forest 'example.test'
+    """
+
+    errno = 3016
+    format = _("Domain '%(domain)s' is not a root domain for forest '%(forest)s'")
+
 
 ##############################################################################
 # 4000 - 4999: Execution errors
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 118b657..e779a12 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -1150,6 +1150,9 @@ class TrustDomainJoins(object):
                 realm_passwd
             )
 
+        if self.remote_domain.info['dns_domain'] != self.remote_domain.info['dns_forest']:
+            raise errors.NotAForestRootError(forest=self.remote_domain.info['dns_forest'], domain=self.remote_domain.info['dns_domain'])
+
         if not self.remote_domain.read_only:
             trustdom_pass = samba.generate_random_password(128, 128)
             self.get_realmdomains()
@@ -1166,5 +1169,8 @@ class TrustDomainJoins(object):
         if not(isinstance(self.remote_domain, TrustDomainInstance)):
             self.populate_remote_domain(realm, realm_server, realm_passwd=None)
 
+        if self.remote_domain.info['dns_domain'] != self.remote_domain.info['dns_forest']:
+            raise errors.NotAForestRootError(forest=self.remote_domain.info['dns_forest'], domain=self.remote_domain.info['dns_domain'])
+
         self.local_domain.establish_trust(self.remote_domain, trustdom_passwd)
         return dict(local=self.local_domain, remote=self.remote_domain, verified=False)
-- 
1.9.3



More information about the Freeipa-devel mailing list