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