Blob Blame History Raw
From 7c4828f3eb0e7b1f246f6263bdf22592d51824df Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy@redhat.com>
Date: Wed, 27 Nov 2013 12:17:43 +0200
Subject: [PATCH 08/10] subdomains: Use AD admin credentials when trust is
 being established

When AD administrator credentials passed, they stored in realm_passwd,
not realm_password in the options.

When passing credentials to ipaserver.dcerpc.fetch_domains(), make sure
to normalize them.

Additionally, force Samba auth module to use NTLMSSP in case we have
credentials because at the point when trust is established, KDC is not
yet ready to issue tickets to a service in the other realm due to
MS-PAC information caching effects. The logic is a bit fuzzy because
credentials code makes decisions on what to use based on the smb.conf
parameters and Python bindings to set parameters to smb.conf make it so
that auth module believes these parameters were overidden by the user
through the command line and ignore some of options. We have to do calls
in the right order to force NTLMSSP use instead of Kerberos.

Fixes https://fedorahosted.org/freeipa/ticket/4046
---
 ipalib/plugins/trust.py | 13 ++++++++++---
 ipaserver/dcerpc.py     | 42 ++++++++++++++++++++++++++++--------------
 2 files changed, 38 insertions(+), 17 deletions(-)

diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py
index 5ba0905030c700c7f63003eef25891c52330934b..3b1b2fc67ce333751556a5c3a59a7f89efc608f9 100644
--- a/ipalib/plugins/trust.py
+++ b/ipalib/plugins/trust.py
@@ -1231,10 +1231,17 @@ def execute(self, *keys, **options):
 def fetch_domains_from_trust(self, trustinstance, trust_entry, **options):
     trust_name = trust_entry['cn'][0]
     creds = None
-    password = options.get('realm_password', None)
+    password = options.get('realm_passwd', None)
     if password:
-        creds = u"%s%%%s" % (options.get('realm_admin'), password)
-    domains = ipaserver.dcerpc.fetch_domains(self.api, trustinstance.local_flatname, trust_name, creds=creds)
+        admin_name = options.get('realm_admin')
+        sp = admin_name.split('\\')
+        if len(sp) == 1:
+            sp.insert(0, trustinstance.remote_domain.info['name'])
+        creds = u"{name}%{password}".format(name="\\".join(sp),
+                                            password=password)
+    domains = ipaserver.dcerpc.fetch_domains(self.api,
+                                             trustinstance.local_flatname,
+                                             trust_name, creds=creds)
     result = []
     if not domains:
         return None
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 0dde3473b12b857ff269a936ad9a07d098405c45..d809c416baac072a2489fbd3c167f08665b7a24e 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -655,7 +655,7 @@ def __gen_lsa_connection(self, binding):
        except RuntimeError, (num, message):
            raise assess_dcerpc_exception(num=num, message=message)
 
-    def __init_lsa_pipe(self, remote_host):
+    def init_lsa_pipe(self, remote_host):
         """
         Try to initialize connection to the LSA pipe at remote host.
         This method tries consequently all possible transport options
@@ -692,7 +692,7 @@ def __gen_lsa_bindings(self, remote_host):
         """
         There are multiple transports to issue LSA calls. However, depending on a
         system in use they may be blocked by local operating system policies.
-        Generate all we can use. __init_lsa_pipe() will try them one by one until
+        Generate all we can use. init_lsa_pipe() will try them one by one until
         there is one working.
 
         We try NCACN_NP before NCACN_IP_TCP and signed sessions before unsigned.
@@ -753,7 +753,7 @@ def parse_naming_context(self, context):
         return naming_ref.match(context).group(1)
 
     def retrieve(self, remote_host):
-        self.__init_lsa_pipe(remote_host)
+        self.init_lsa_pipe(remote_host)
 
         objectAttribute = lsa.ObjectAttribute()
         objectAttribute.sec_qos = lsa.QosInfo()
@@ -964,34 +964,48 @@ def fetch_domains(api, mydomain, trustdomain, creds=None):
                 NETR_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL  = 0x00000040)
 
     def communicate(td):
-        td.creds.guess(td.parm)
-        netrc = net.Net(creds=td.creds, lp=td.parm)
-        try:
-            result = netrc.finddc(domain=trustdomain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
-        except RuntimeError, e:
-            raise assess_dcerpc_exception(message=str(e))
-        if not result:
-            return None
-        td.retrieve(unicode(result.pdc_dns_name))
-
+        td.init_lsa_pipe(td.info['dc'])
         netr_pipe = netlogon.netlogon(td.binding, td.parm, td.creds)
         domains = netr_pipe.netr_DsrEnumerateDomainTrusts(td.binding, 1)
         return domains
 
     domains = None
+    domain_validator = DomainValidator(api)
+    configured = domain_validator.is_configured()
+    if not configured:
+        return None
+
     td = TrustDomainInstance('')
     td.parm.set('workgroup', mydomain)
-    td.creds = credentials.Credentials()
+    cr = credentials.Credentials()
+    cr.set_kerberos_state(credentials.DONT_USE_KERBEROS)
+    cr.guess(td.parm)
+    cr.set_anonymous()
+    cr.set_workstation(domain_validator.flatname)
+    netrc = net.Net(creds=cr, lp=td.parm)
+    try:
+        result = netrc.finddc(domain=trustdomain,
+                              flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
+    except RuntimeError, e:
+        raise assess_dcerpc_exception(message=str(e))
+
+    td.info['dc'] = unicode(result.pdc_dns_name)
     if creds is None:
         domval = DomainValidator(api)
         (ccache_name, principal) = domval.kinit_as_http(trustdomain)
+        td.creds = credentials.Credentials()
         td.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS)
         if ccache_name:
             with installutils.private_ccache(path=ccache_name):
+                td.creds.guess(td.parm)
+                td.creds.set_workstation(domain_validator.flatname)
                 domains = communicate(td)
     else:
+        td.creds = credentials.Credentials()
         td.creds.set_kerberos_state(credentials.DONT_USE_KERBEROS)
+        td.creds.guess(td.parm)
         td.creds.parse_string(creds)
+        td.creds.set_workstation(domain_validator.flatname)
         domains = communicate(td)
 
     if domains is None:
-- 
1.8.3.1