c58629
From a4e9e8f368c8a61d539e9da26e4a16a6234c138f Mon Sep 17 00:00:00 2001
c58629
From: Alexander Bokovoy <abokovoy@redhat.com>
c58629
Date: Fri, 17 Nov 2017 17:19:25 +0200
c58629
Subject: [PATCH] trust: detect and error out when non-AD trust with IPA domain
c58629
 name exists
c58629
c58629
Quite often users choose wrong type of trust on Active Directory side
c58629
when setting up a trust to freeIPA. The trust type supported by freeIPA
c58629
is just a normal forest trust to another Active Directory. However,
c58629
some people follow old internet recipes that force using a trust to MIT
c58629
Kerberos realm.
c58629
c58629
This is a wrong type of trust. Unfortunately, when someone used MIT
c58629
Kerberos realm trust, there is no way to programmatically remote the
c58629
trust from freeIPA side. As result, we have to detect such situation and
c58629
report an error.
c58629
c58629
To do proper reporting, we need reuse some constants and trust type
c58629
names we use in IPA CLI/Web UI. These common components were moved to
c58629
a separate ipaserver/dcerpc_common.py module that is imported by both
c58629
ipaserver/plugins/trust.py and ipaserver/dcerpc.py.
c58629
c58629
Fixes https://pagure.io/freeipa/issue/7264
c58629
c58629
Reviewed-By: Christian Heimes <cheimes@redhat.com>
c58629
Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
c58629
---
c58629
 ipaserver/dcerpc.py        | 37 +++++++++++++++--------
c58629
 ipaserver/dcerpc_common.py | 73 ++++++++++++++++++++++++++++++++++++++++++++++
c58629
 ipaserver/plugins/trust.py | 65 ++++++++++-------------------------------
c58629
 3 files changed, 113 insertions(+), 62 deletions(-)
c58629
 create mode 100644 ipaserver/dcerpc_common.py
c58629
c58629
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
c58629
index aa63cd9db0a1d47b5309cc6bed2ff7584760a39d..ac1b2a34784df491a3851aa21bbadbec2297241c 100644
c58629
--- a/ipaserver/dcerpc.py
c58629
+++ b/ipaserver/dcerpc.py
c58629
@@ -31,6 +31,10 @@ from ipapython import ipautil
c58629
 from ipapython.ipa_log_manager import root_logger
c58629
 from ipapython.dn import DN
c58629
 from ipaserver.install import installutils
c58629
+from ipaserver.dcerpc_common import (TRUST_BIDIRECTIONAL,
c58629
+                                     TRUST_JOIN_EXTERNAL,
c58629
+                                     trust_type_string)
c58629
+
c58629
 from ipalib.util import normalize_name
c58629
 
c58629
 import os
c58629
@@ -77,15 +81,6 @@ The code in this module relies heavily on samba4-python package
c58629
 and Samba4 python bindings.
c58629
 """)
c58629
 
c58629
-# Both constants can be used as masks against trust direction
c58629
-# because bi-directional has two lower bits set.
c58629
-TRUST_ONEWAY = 1
c58629
-TRUST_BIDIRECTIONAL = 3
c58629
-
c58629
-# Trust join behavior
c58629
-# External trust -- allow creating trust to a non-root domain in the forest
c58629
-TRUST_JOIN_EXTERNAL = 1
c58629
-
c58629
 
c58629
 def is_sid_valid(sid):
c58629
     try:
c58629
@@ -151,6 +146,7 @@ pysss_type_key_translation_dict = {
c58629
     pysss_nss_idmap.ID_BOTH: 'both',
c58629
 }
c58629
 
c58629
+
c58629
 class TrustTopologyConflictSolved(Exception):
c58629
     """
c58629
     Internal trust error: raised when previously detected
c58629
@@ -1254,9 +1250,26 @@ class TrustDomainInstance(object):
c58629
             dname = lsa.String()
c58629
             dname.string = another_domain.info['dns_domain']
c58629
             res = self._pipe.QueryTrustedDomainInfoByName(
c58629
-                                self._policy_handle,
c58629
-                                dname,
c58629
-                                lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
c58629
+                self._policy_handle,
c58629
+                dname,
c58629
+                lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO
c58629
+            )
c58629
+            if res.info_ex.trust_type != lsa.LSA_TRUST_TYPE_UPLEVEL:
c58629
+                msg = _('There is already a trust to {ipa_domain} with '
c58629
+                        'unsupported type {trust_type}. Please remove '
c58629
+                        'it manually on AD DC side.')
c58629
+                ttype = trust_type_string(
c58629
+                    res.info_ex.trust_type, res.info_ex.trust_attributes
c58629
+                )
c58629
+                err = unicode(msg).format(
c58629
+                    ipa_domain=another_domain.info['dns_domain'],
c58629
+                    trust_type=ttype)
c58629
+
c58629
+                raise errors.ValidationError(
c58629
+                    name=_('AD domain controller'),
c58629
+                    error=err
c58629
+                )
c58629
+
c58629
             self._pipe.DeleteTrustedDomain(self._policy_handle,
c58629
                                            res.info_ex.sid)
c58629
         except RuntimeError as e:
c58629
diff --git a/ipaserver/dcerpc_common.py b/ipaserver/dcerpc_common.py
c58629
new file mode 100644
c58629
index 0000000000000000000000000000000000000000..526b025e3282c8a556088eb2ed1ba467b889b86c
c58629
--- /dev/null
c58629
+++ b/ipaserver/dcerpc_common.py
c58629
@@ -0,0 +1,73 @@
c58629
+import six
c58629
+from ipalib import _
c58629
+if six.PY3:
c58629
+    unicode = six.text_type
c58629
+
c58629
+# Both constants can be used as masks against trust direction
c58629
+# because bi-directional has two lower bits set.
c58629
+TRUST_ONEWAY = 1
c58629
+TRUST_BIDIRECTIONAL = 3
c58629
+
c58629
+# Trust join behavior
c58629
+# External trust -- allow creating trust to a non-root domain in the forest
c58629
+TRUST_JOIN_EXTERNAL = 1
c58629
+
c58629
+# We don't want to import any of Samba Python code here just for constants
c58629
+# Since these constants set in MS-ADTS, we can rely on their stability
c58629
+LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE = 0x00000001
c58629
+
c58629
+_trust_direction_dict = {
c58629
+        1: _('Trusting forest'),
c58629
+        2: _('Trusted forest'),
c58629
+        3: _('Two-way trust')
c58629
+}
c58629
+
c58629
+_trust_status_dict = {
c58629
+        True: _('Established and verified'),
c58629
+        False: _('Waiting for confirmation by remote side')
c58629
+}
c58629
+
c58629
+_trust_type_dict_unknown = _('Unknown')
c58629
+
c58629
+# Trust type is a combination of ipanttrusttype and ipanttrustattributes
c58629
+# We shift trust attributes by 3 bits to left so bit 0 becomes bit 3 and
c58629
+# 2+(1 << 3) becomes 10.
c58629
+_trust_type_dict = {
c58629
+        1: _('Non-Active Directory domain'),
c58629
+        2: _('Active Directory domain'),
c58629
+        3: _('RFC4120-compliant Kerberos realm'),
c58629
+        10: _('Non-transitive external trust to a domain in '
c58629
+              'another Active Directory forest'),
c58629
+        11: _('Non-transitive external trust to an RFC4120-'
c58629
+              'compliant Kerberos realm')
c58629
+}
c58629
+
c58629
+
c58629
+def trust_type_string(level, attrs):
c58629
+    """
c58629
+    Returns a string representing a type of the trust.
c58629
+    The original field is an enum:
c58629
+      LSA_TRUST_TYPE_DOWNLEVEL  = 0x00000001,
c58629
+      LSA_TRUST_TYPE_UPLEVEL    = 0x00000002,
c58629
+      LSA_TRUST_TYPE_MIT        = 0x00000003
c58629
+    """
c58629
+    transitive = int(attrs) & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
c58629
+    string = _trust_type_dict.get(int(level) | (transitive << 3),
c58629
+                                  _trust_type_dict_unknown)
c58629
+    return unicode(string)
c58629
+
c58629
+
c58629
+def trust_direction_string(level):
c58629
+    """
c58629
+    Returns a string representing a direction of the trust.
c58629
+    The original field is a bitmask taking two bits in use
c58629
+      LSA_TRUST_DIRECTION_INBOUND  = 0x00000001,
c58629
+      LSA_TRUST_DIRECTION_OUTBOUND = 0x00000002
c58629
+    """
c58629
+    string = _trust_direction_dict.get(int(level), _trust_type_dict_unknown)
c58629
+    return unicode(string)
c58629
+
c58629
+
c58629
+def trust_status_string(level):
c58629
+    string = _trust_status_dict.get(level, _trust_type_dict_unknown)
c58629
+    return unicode(string)
c58629
diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py
c58629
index d0bbfbc47ca65c9c5229685fc9d202c293fe41cd..ecc5fa0b22f94de05cc5282758be093f0cfca13f 100644
c58629
--- a/ipaserver/plugins/trust.py
c58629
+++ b/ipaserver/plugins/trust.py
c58629
@@ -44,6 +44,13 @@ from ipalib import errors
c58629
 from ipalib import output
c58629
 from ldap import SCOPE_SUBTREE
c58629
 from time import sleep
c58629
+from ipaserver.dcerpc_common import (TRUST_ONEWAY,
c58629
+                                     TRUST_BIDIRECTIONAL,
c58629
+                                     TRUST_JOIN_EXTERNAL,
c58629
+                                     LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE,
c58629
+                                     trust_type_string,
c58629
+                                     trust_direction_string,
c58629
+                                     trust_status_string)
c58629
 
c58629
 if six.PY3:
c58629
     unicode = str
c58629
@@ -63,9 +70,6 @@ except Exception as e:
c58629
 if api.env.in_server and api.env.context in ['lite', 'server']:
c58629
     try:
c58629
         import ipaserver.dcerpc
c58629
-        from ipaserver.dcerpc import (TRUST_ONEWAY,
c58629
-                                      TRUST_BIDIRECTIONAL,
c58629
-                                      TRUST_JOIN_EXTERNAL)
c58629
         import dbus
c58629
         import dbus.mainloop.glib
c58629
         _bindings_installed = True
c58629
@@ -157,28 +161,14 @@ particular type.
c58629
 
c58629
 register = Registry()
c58629
 
c58629
-# Trust type is a combination of ipanttrusttype and ipanttrustattributes
c58629
-# We shift trust attributes by 3 bits to left so bit 0 becomes bit 3 and
c58629
-# 2+(1 << 3) becomes 10.
c58629
-_trust_type_dict = {1 : _('Non-Active Directory domain'),
c58629
-                    2 : _('Active Directory domain'),
c58629
-                    3 : _('RFC4120-compliant Kerberos realm'),
c58629
-                    10: _('Non-transitive external trust to a domain in another Active Directory forest')}
c58629
-
c58629
-_trust_direction_dict = {1 : _('Trusting forest'),
c58629
-                         2 : _('Trusted forest'),
c58629
-                         3 : _('Two-way trust')}
c58629
-_trust_status_dict = {True : _('Established and verified'),
c58629
-                 False : _('Waiting for confirmation by remote side')}
c58629
-_trust_type_dict_unknown = _('Unknown')
c58629
-
c58629
-_trust_type_option = StrEnum('trust_type',
c58629
-                        cli_name='type',
c58629
-                        label=_('Trust type (ad for Active Directory, default)'),
c58629
-                        values=(u'ad',),
c58629
-                        default=u'ad',
c58629
-                        autofill=True,
c58629
-                    )
c58629
+_trust_type_option = StrEnum(
c58629
+         'trust_type',
c58629
+         cli_name='type',
c58629
+         label=_('Trust type (ad for Active Directory, default)'),
c58629
+         values=(u'ad',),
c58629
+         default=u'ad',
c58629
+         autofill=True,
c58629
+        )
c58629
 
c58629
 DEFAULT_RANGE_SIZE = 200000
c58629
 
c58629
@@ -187,31 +177,6 @@ DBUS_IFACE_TRUST = 'com.redhat.idm.trust'
c58629
 CRED_STYLE_SAMBA = 1
c58629
 CRED_STYLE_KERBEROS = 2
c58629
 
c58629
-LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE = 0x00000001
c58629
-
c58629
-def trust_type_string(level, attrs):
c58629
-    """
c58629
-    Returns a string representing a type of the trust. The original field is an enum:
c58629
-      LSA_TRUST_TYPE_DOWNLEVEL  = 0x00000001,
c58629
-      LSA_TRUST_TYPE_UPLEVEL    = 0x00000002,
c58629
-      LSA_TRUST_TYPE_MIT        = 0x00000003
c58629
-    """
c58629
-    transitive = int(attrs) & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
c58629
-    string = _trust_type_dict.get(int(level) | (transitive << 3), _trust_type_dict_unknown)
c58629
-    return unicode(string)
c58629
-
c58629
-def trust_direction_string(level):
c58629
-    """
c58629
-    Returns a string representing a direction of the trust. The original field is a bitmask taking two bits in use
c58629
-      LSA_TRUST_DIRECTION_INBOUND  = 0x00000001,
c58629
-      LSA_TRUST_DIRECTION_OUTBOUND = 0x00000002
c58629
-    """
c58629
-    string = _trust_direction_dict.get(int(level), _trust_type_dict_unknown)
c58629
-    return unicode(string)
c58629
-
c58629
-def trust_status_string(level):
c58629
-    string = _trust_status_dict.get(level, _trust_type_dict_unknown)
c58629
-    return unicode(string)
c58629
 
c58629
 def make_trust_dn(env, trust_type, dn):
c58629
     assert isinstance(dn, DN)
c58629
-- 
c58629
2.13.6
c58629