From cf17b7af5a8cd835ae20fe23a57fe656481e27a3 Mon Sep 17 00:00:00 2001 From: Alexander Bokovoy Date: Jan 13 2021 18:56:13 +0000 Subject: ipaserver/dcerpc: use Samba-provided trust helper to establish trust When establishing trust to Active Directory forest, RC4 is used to encrypt trusted domain object credentials as an application-specific material in a secure channel based on AES session key. In FIPS mode it is not possible to use RC4 directly. Samba 4.14 and backports to 4.13 in Fedora 33+ and RHEL 8.4+ now provide a helper that wraps LSA RPC call CreateTrustedDomainEx2. This helper ensures that in FIPS mode we first check that LSA session key is AES before allowing RC4 use internally in Samba bindings. Thus, it becomes possible to establish trust to Active Directory forest in FIPS mode. Adopt FreeIPA code to use the helper provided by Samba when it is available. If neither the helper nor unprotected arcfour_encrypt utility is available from Samba bindings, fail import of the ipaserver.dcerpc module. Fixes: https://pagure.io/freeipa/issue/8655 Signed-off-by: Alexander Bokovoy Reviewed-By: Rob Crittenden --- diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py index ff56270..466491a 100644 --- a/ipaserver/dcerpc.py +++ b/ipaserver/dcerpc.py @@ -50,10 +50,19 @@ from samba import credentials 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 +try: + from samba.trust_utils import CreateTrustedDomainRelax +except ImportError: + CreateTrustedDomainRelax = None +try: + from samba import arcfour_encrypt +except ImportError: + if CreateTrustedDomainRelax is None: + raise ImportError("No supported Samba Python bindings") + import ldap as _ldap from ipapython import ipaldap from ipapython.dnsutil import DNSName @@ -1021,29 +1030,34 @@ class TrustDomainInstance: outgoing = drsblobs.trustAuthInOutBlob() outgoing.count = 1 outgoing.current = authinfo_array + self.auth_inoutblob = outgoing - confounder = [3]*512 - for i in range(512): - confounder[i] = random.randint(0, 255) + if CreateTrustedDomainRelax is None: + # Samba Python bindings with no support for FIPS wrapper + # We have to generate AuthInfo ourselves which means + # we have to use RC4 encryption directly + confounder = [3] * 512 + for i in range(512): + confounder[i] = random.randint(0, 255) - trustpass = drsblobs.trustDomainPasswords() - trustpass.confounder = confounder + trustpass = drsblobs.trustDomainPasswords() + trustpass.confounder = confounder - trustpass.outgoing = outgoing - trustpass.incoming = outgoing + trustpass.outgoing = outgoing + trustpass.incoming = outgoing - trustpass_blob = ndr_pack(trustpass) + trustpass_blob = ndr_pack(trustpass) - encrypted_trustpass = arcfour_encrypt(self._pipe.session_key, - trustpass_blob) + encrypted_trustpass = arcfour_encrypt(self._pipe.session_key, + trustpass_blob) - auth_blob = lsa.DATA_BUF2() - auth_blob.size = len(encrypted_trustpass) - auth_blob.data = string_to_array(encrypted_trustpass) + auth_blob = lsa.DATA_BUF2() + auth_blob.size = len(encrypted_trustpass) + auth_blob.data = string_to_array(encrypted_trustpass) - auth_info = lsa.TrustDomainInfoAuthInfoInternal() - auth_info.auth_blob = auth_blob - self.auth_info = auth_info + auth_info = lsa.TrustDomainInfoAuthInfoInternal() + auth_info.auth_blob = auth_blob + self.auth_info = auth_info def generate_ftinfo(self, another_domain): """ @@ -1311,7 +1325,6 @@ class TrustDomainInstance: 'the same NetBIOS name: %s') % self.info['name']) - self.generate_auth(trustdom_secret) info = lsa.TrustDomainInfoInfoEx() info.domain_name.string = another_domain.info['dns_domain'] @@ -1360,10 +1373,19 @@ class TrustDomainInstance: raise access_denied_error try: - trustdom_handle = self._pipe.CreateTrustedDomainEx2( - self._policy_handle, - info, self.auth_info, - security.SEC_STD_DELETE) + self.generate_auth(trustdom_secret) + if CreateTrustedDomainRelax is not None: + trustdom_handle = CreateTrustedDomainRelax( + self._pipe, self._policy_handle, info, + security.SEC_STD_DELETE, + self.auth_inoutblob, self.auth_inoutblob) + else: + # Samba Python bindings with no support for FIPS wrapper + # We keep using older code + trustdom_handle = self._pipe.CreateTrustedDomainEx2( + self._policy_handle, + info, self.auth_info, + security.SEC_STD_DELETE) except RuntimeError as e: raise assess_dcerpc_error(e)