|
|
86baa9 |
From baf93c9bdfcb761ab469efdb2a1ba4ae8485f165 Mon Sep 17 00:00:00 2001
|
|
|
86baa9 |
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
|
86baa9 |
Date: Fri, 22 Mar 2019 19:02:16 +0200
|
|
|
86baa9 |
Subject: [PATCH] upgrade: upgrade existing trust agreements to new layout
|
|
|
86baa9 |
|
|
|
86baa9 |
Existing trust agreements will lack required Kerberos principals and
|
|
|
86baa9 |
POSIX attributes expected to allow Active Directory domain controllers
|
|
|
86baa9 |
to query IPA master over LSA and NETLOGON RPC pipes.
|
|
|
86baa9 |
|
|
|
86baa9 |
Upgrade code is split into two parts:
|
|
|
86baa9 |
- upgrade trusted domain object to have proper POSIX attributes
|
|
|
86baa9 |
- generate required Kerberos principals for AD DC communication
|
|
|
86baa9 |
|
|
|
86baa9 |
Fixes: https://pagure.io/freeipa/issue/6077
|
|
|
86baa9 |
(cherry picked from commit 090e09df130f27fb30a986529ef0944383e668e1)
|
|
|
86baa9 |
|
|
|
86baa9 |
Reviewed-By: Christian Heimes <cheimes@redhat.com>
|
|
|
86baa9 |
---
|
|
|
86baa9 |
ipaserver/install/plugins/adtrust.py | 371 ++++++++++++++++++++++++---
|
|
|
86baa9 |
1 file changed, 329 insertions(+), 42 deletions(-)
|
|
|
86baa9 |
|
|
|
86baa9 |
diff --git a/ipaserver/install/plugins/adtrust.py b/ipaserver/install/plugins/adtrust.py
|
|
|
86baa9 |
index 1f50bef891770c53a9086c7aa36d0ee1f088fbe6..1461e000dc855a21665eb5ea0cfe4a47df419344 100644
|
|
|
86baa9 |
--- a/ipaserver/install/plugins/adtrust.py
|
|
|
86baa9 |
+++ b/ipaserver/install/plugins/adtrust.py
|
|
|
86baa9 |
@@ -1,30 +1,27 @@
|
|
|
86baa9 |
-# Authors:
|
|
|
86baa9 |
-# Martin Kosek <mkosek@redhat.com>
|
|
|
86baa9 |
-#
|
|
|
86baa9 |
-# Copyright (C) 2012 Red Hat
|
|
|
86baa9 |
-# see file 'COPYING' for use and warranty information
|
|
|
86baa9 |
-#
|
|
|
86baa9 |
-# This program is free software; you can redistribute it and/or modify
|
|
|
86baa9 |
-# it under the terms of the GNU General Public License as published by
|
|
|
86baa9 |
-# the Free Software Foundation, either version 3 of the License, or
|
|
|
86baa9 |
-# (at your option) any later version.
|
|
|
86baa9 |
-#
|
|
|
86baa9 |
-# This program is distributed in the hope that it will be useful,
|
|
|
86baa9 |
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
86baa9 |
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
86baa9 |
-# GNU General Public License for more details.
|
|
|
86baa9 |
-#
|
|
|
86baa9 |
-# You should have received a copy of the GNU General Public License
|
|
|
86baa9 |
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
86baa9 |
+# Copyright (C) 2012-2019 FreeIPA Contributors see COPYING for license
|
|
|
86baa9 |
|
|
|
86baa9 |
+from __future__ import absolute_import
|
|
|
86baa9 |
import logging
|
|
|
86baa9 |
|
|
|
86baa9 |
from ipalib import Registry, errors
|
|
|
86baa9 |
from ipalib import Updater
|
|
|
86baa9 |
from ipapython.dn import DN
|
|
|
86baa9 |
+from ipapython import ipautil
|
|
|
86baa9 |
+from ipaplatform.paths import paths
|
|
|
86baa9 |
from ipaserver.install import sysupgrade
|
|
|
86baa9 |
from ipaserver.install.adtrustinstance import (
|
|
|
86baa9 |
ADTRUSTInstance, map_Guests_to_nobody)
|
|
|
86baa9 |
+from ipaserver.dcerpc_common import TRUST_BIDIRECTIONAL
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+try:
|
|
|
86baa9 |
+ from samba.ndr import ndr_unpack
|
|
|
86baa9 |
+ from samba.dcerpc import lsa, drsblobs
|
|
|
86baa9 |
+except ImportError:
|
|
|
86baa9 |
+ # If samba.ndr is not available, this machine is not provisioned
|
|
|
86baa9 |
+ # for serving a trust to Active Directory. As result, it does
|
|
|
86baa9 |
+ # not matter what ndr_unpack does but we save on pylint checks
|
|
|
86baa9 |
+ def ndr_unpack(x):
|
|
|
86baa9 |
+ raise NotImplementedError
|
|
|
86baa9 |
|
|
|
86baa9 |
logger = logging.getLogger(__name__)
|
|
|
86baa9 |
|
|
|
86baa9 |
@@ -325,6 +322,28 @@ class update_sids(Updater):
|
|
|
86baa9 |
return False, ()
|
|
|
86baa9 |
|
|
|
86baa9 |
|
|
|
86baa9 |
+def get_gidNumber(ldap, env):
|
|
|
86baa9 |
+ # Read the gidnumber of the fallback group and returns a list with it
|
|
|
86baa9 |
+ dn = DN(('cn', ADTRUSTInstance.FALLBACK_GROUP_NAME),
|
|
|
86baa9 |
+ env.container_group,
|
|
|
86baa9 |
+ env.basedn)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ try:
|
|
|
86baa9 |
+ entry = ldap.get_entry(dn, ['gidnumber'])
|
|
|
86baa9 |
+ gidNumber = entry.get('gidnumber')
|
|
|
86baa9 |
+ except errors.NotFound:
|
|
|
86baa9 |
+ logger.error("%s not found",
|
|
|
86baa9 |
+ ADTRUSTInstance.FALLBACK_GROUP_NAME)
|
|
|
86baa9 |
+ return None
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ if gidNumber is None:
|
|
|
86baa9 |
+ logger.error("%s does not have a gidnumber",
|
|
|
86baa9 |
+ ADTRUSTInstance.FALLBACK_GROUP_NAME)
|
|
|
86baa9 |
+ return None
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ return gidNumber
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+
|
|
|
86baa9 |
@register()
|
|
|
86baa9 |
class update_tdo_gidnumber(Updater):
|
|
|
86baa9 |
"""
|
|
|
86baa9 |
@@ -340,43 +359,55 @@ class update_tdo_gidnumber(Updater):
|
|
|
86baa9 |
logger.debug('AD Trusts are not enabled on this server')
|
|
|
86baa9 |
return False, []
|
|
|
86baa9 |
|
|
|
86baa9 |
- # Read the gidnumber of the fallback group
|
|
|
86baa9 |
- dn = DN(('cn', ADTRUSTInstance.FALLBACK_GROUP_NAME),
|
|
|
86baa9 |
- self.api.env.container_group,
|
|
|
86baa9 |
- self.api.env.basedn)
|
|
|
86baa9 |
-
|
|
|
86baa9 |
- try:
|
|
|
86baa9 |
- entry = ldap.get_entry(dn, ['gidnumber'])
|
|
|
86baa9 |
- gidNumber = entry.get('gidnumber')
|
|
|
86baa9 |
- except errors.NotFound:
|
|
|
86baa9 |
- logger.error("%s not found",
|
|
|
86baa9 |
- ADTRUSTInstance.FALLBACK_GROUP_NAME)
|
|
|
86baa9 |
- return False, ()
|
|
|
86baa9 |
-
|
|
|
86baa9 |
+ gidNumber = get_gidNumber(ldap, self.api.env)
|
|
|
86baa9 |
if not gidNumber:
|
|
|
86baa9 |
logger.error("%s does not have a gidnumber",
|
|
|
86baa9 |
ADTRUSTInstance.FALLBACK_GROUP_NAME)
|
|
|
86baa9 |
return False, ()
|
|
|
86baa9 |
|
|
|
86baa9 |
- # For each trusted domain object, add gidNumber
|
|
|
86baa9 |
+ # For each trusted domain object, add posix attributes
|
|
|
86baa9 |
+ # to allow use of a trusted domain account by AD DCs
|
|
|
86baa9 |
+ # to authenticate against our Samba instance
|
|
|
86baa9 |
try:
|
|
|
86baa9 |
tdos = ldap.get_entries(
|
|
|
86baa9 |
DN(self.api.env.container_adtrusts, self.api.env.basedn),
|
|
|
86baa9 |
scope=ldap.SCOPE_ONELEVEL,
|
|
|
86baa9 |
- filter="(objectclass=ipaNTTrustedDomain)",
|
|
|
86baa9 |
- attrs_list=['gidnumber'])
|
|
|
86baa9 |
+ filter="(&(objectclass=ipaNTTrustedDomain)"
|
|
|
86baa9 |
+ "(objectclass=ipaIDObject))",
|
|
|
86baa9 |
+ attrs_list=['gidnumber', 'uidnumber', 'objectclass',
|
|
|
86baa9 |
+ 'ipantsecurityidentifier',
|
|
|
86baa9 |
+ 'ipaNTTrustDirection'
|
|
|
86baa9 |
+ 'uid', 'cn', 'ipantflatname'])
|
|
|
86baa9 |
for tdo in tdos:
|
|
|
86baa9 |
# if the trusted domain object does not contain gidnumber,
|
|
|
86baa9 |
# add the default fallback group gidnumber
|
|
|
86baa9 |
if not tdo.get('gidnumber'):
|
|
|
86baa9 |
- try:
|
|
|
86baa9 |
- tdo['gidnumber'] = gidNumber
|
|
|
86baa9 |
- ldap.update_entry(tdo)
|
|
|
86baa9 |
- logger.debug("Added gidnumber %s to %s",
|
|
|
86baa9 |
- gidNumber, tdo.dn)
|
|
|
86baa9 |
- except Exception:
|
|
|
86baa9 |
- logger.warning(
|
|
|
86baa9 |
- "Failed to add gidnumber to %s", tdo.dn)
|
|
|
86baa9 |
+ tdo['gidnumber'] = gidNumber
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # Generate uidNumber and ipaNTSecurityIdentifier if
|
|
|
86baa9 |
+ # uidNumber is missing. We rely on sidgen plugin here
|
|
|
86baa9 |
+ # to generate ipaNTSecurityIdentifier.
|
|
|
86baa9 |
+ if not tdo.get('uidnumber'):
|
|
|
86baa9 |
+ tdo['uidnumber'] = ['-1']
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ if 'posixAccount' not in tdo.get('objectclass'):
|
|
|
86baa9 |
+ tdo['objectclass'].extend(['posixAccount'])
|
|
|
86baa9 |
+ # Based on the flat name of a TDO,
|
|
|
86baa9 |
+ # add user name FLATNAME$ (note dollar sign)
|
|
|
86baa9 |
+ # to allow SSSD to map this TDO to a POSIX account
|
|
|
86baa9 |
+ if not tdo.get('uid'):
|
|
|
86baa9 |
+ tdo['uid'] = ["{flatname}$".format(
|
|
|
86baa9 |
+ flatname=tdo.single_value['ipantflatname'])]
|
|
|
86baa9 |
+ if not tdo.get('homedirectory'):
|
|
|
86baa9 |
+ tdo['homedirectory'] = ['/dev/null']
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # Store resulted entry
|
|
|
86baa9 |
+ try:
|
|
|
86baa9 |
+ ldap.update_entry(tdo)
|
|
|
86baa9 |
+ except errors.ExecutionError as e:
|
|
|
86baa9 |
+ logger.warning(
|
|
|
86baa9 |
+ "Failed to update trusted domain object %s", tdo.dn)
|
|
|
86baa9 |
+ logger.debug("Exception during TDO update: %s", str(e))
|
|
|
86baa9 |
|
|
|
86baa9 |
except errors.NotFound:
|
|
|
86baa9 |
logger.debug("No trusted domain object to update")
|
|
|
86baa9 |
@@ -400,3 +431,259 @@ class update_mapping_Guests_to_nobody(Updater):
|
|
|
86baa9 |
|
|
|
86baa9 |
map_Guests_to_nobody()
|
|
|
86baa9 |
return False, []
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+@register()
|
|
|
86baa9 |
+class update_tdo_to_new_layout(Updater):
|
|
|
86baa9 |
+ """
|
|
|
86baa9 |
+ Transform trusted domain objects into a new layout
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ There are now two Kerberos principals per direction of trust:
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ INBOUND:
|
|
|
86baa9 |
+ - krbtgt/<OUR REALM>@<REMOTE REALM>, enabled by default
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ - <OUR FLATNAME$>@<REMOTE REALM>, disabled by default on our side
|
|
|
86baa9 |
+ as it is only used by SSSD to retrieve TDO creds when operating
|
|
|
86baa9 |
+ as an AD Trust agent across IPA topology
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ OUTBOUND:
|
|
|
86baa9 |
+ - krbtgt/<REMOTE REALM>@<OUR REALM>, enabled by default
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ - <REMOTE FLATNAME$>@<OUR REALM>, enabled by default and
|
|
|
86baa9 |
+ used by remote trusted DCs to authenticate against us
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ This principal also has krbtgt/<REMOTE FLATNAME>@<OUR REALM> defined
|
|
|
86baa9 |
+ as a Kerberos principal alias. This is due to how Kerberos
|
|
|
86baa9 |
+ key salt is derived for cross-realm principals on AD side
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ Finally, Samba requires <REMOTE FLATNAME$> account to also possess POSIX
|
|
|
86baa9 |
+ and SMB identities. We ensure this by making the trusted domain object to
|
|
|
86baa9 |
+ be this account with 'uid' and 'cn' attributes being '<REMOTE FLATNAME$>'
|
|
|
86baa9 |
+ and uidNumber/gidNumber generated automatically. Also, we ensure the
|
|
|
86baa9 |
+ trusted domain object is given a SID.
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ The update to <REMOTE FLATNAME$> POSIX/SMB identities is done through
|
|
|
86baa9 |
+ the update plugin update_tdo_gidnumber.
|
|
|
86baa9 |
+ """
|
|
|
86baa9 |
+ tgt_principal_template = "krbtgt/{remote}@{local}"
|
|
|
86baa9 |
+ nbt_principal_template = "{nbt}$@{realm}"
|
|
|
86baa9 |
+ trust_filter = \
|
|
|
86baa9 |
+ "(&(objectClass=ipaNTTrustedDomain)(objectClass=ipaIDObject))"
|
|
|
86baa9 |
+ trust_attrs = ("ipaNTFlatName", "ipaNTTrustPartner", "ipaNTTrustDirection",
|
|
|
86baa9 |
+ "cn", "ipaNTTrustAttributes", "ipaNTAdditionalSuffixes",
|
|
|
86baa9 |
+ "ipaNTTrustedDomainSID", "ipaNTTrustType",
|
|
|
86baa9 |
+ "ipaNTTrustAuthIncoming", "ipaNTTrustAuthOutgoing")
|
|
|
86baa9 |
+ change_password_template = \
|
|
|
86baa9 |
+ "change_password -pw {password} " \
|
|
|
86baa9 |
+ "-e aes256-cts-hmac-sha1-96,aes128-cts-hmac-sha1-96 " \
|
|
|
86baa9 |
+ "{principal}"
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ KRB_PRINC_CREATE_DEFAULT = 0x00000000
|
|
|
86baa9 |
+ KRB_PRINC_CREATE_DISABLED = 0x00000001
|
|
|
86baa9 |
+ KRB_PRINC_CREATE_AGENT_PERMISSION = 0x00000002
|
|
|
86baa9 |
+ KRB_PRINC_CREATE_IDENTITY = 0x00000004
|
|
|
86baa9 |
+ KRB_PRINC_MUST_EXIST = 0x00000008
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # This is a flag for krbTicketFlags attribute
|
|
|
86baa9 |
+ # to disallow creating any tickets using this principal
|
|
|
86baa9 |
+ KRB_DISALLOW_ALL_TIX = 0x00000040
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ def retrieve_trust_password(self, packed):
|
|
|
86baa9 |
+ # The structure of the trust secret is described at
|
|
|
86baa9 |
+ # https://github.com/samba-team/samba/blob/master/
|
|
|
86baa9 |
+ # librpc/idl/drsblobs.idl#L516-L569
|
|
|
86baa9 |
+ # In our case in LDAP TDO object stores
|
|
|
86baa9 |
+ # `struct trustAuthInOutBlob` that has `count` and
|
|
|
86baa9 |
+ # the `current` of `AuthenticationInformationArray` struct
|
|
|
86baa9 |
+ # which has own `count` and `array` of `AuthenticationInformation`
|
|
|
86baa9 |
+ # structs that have `AuthType` field which should be equal to
|
|
|
86baa9 |
+ # `LSA_TRUST_AUTH_TYPE_CLEAR`.
|
|
|
86baa9 |
+ # Then AuthInfo field would contain a password as an array of bytes
|
|
|
86baa9 |
+ assert(packed.count != 0)
|
|
|
86baa9 |
+ assert(packed.current.count != 0)
|
|
|
86baa9 |
+ assert(packed.current.array[0].AuthType == lsa.TRUST_AUTH_TYPE_CLEAR)
|
|
|
86baa9 |
+ clear_value = packed.current.array[0].AuthInfo.password
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ return ''.join(map(chr, clear_value))
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ def set_krb_principal(self, principals, password, trustdn, flags=None):
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ ldap = self.api.Backend.ldap2
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ if isinstance(principals, (list, tuple)):
|
|
|
86baa9 |
+ trust_principal = principals[0]
|
|
|
86baa9 |
+ aliases = principals[1:]
|
|
|
86baa9 |
+ else:
|
|
|
86baa9 |
+ trust_principal = principals
|
|
|
86baa9 |
+ aliases = []
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ try:
|
|
|
86baa9 |
+ entry = ldap.get_entry(
|
|
|
86baa9 |
+ DN(('krbprincipalname', trust_principal), trustdn))
|
|
|
86baa9 |
+ dn = entry.dn
|
|
|
86baa9 |
+ action = ldap.update_entry
|
|
|
86baa9 |
+ logger.debug("Updating Kerberos principal entry for %s",
|
|
|
86baa9 |
+ trust_principal)
|
|
|
86baa9 |
+ except errors.NotFound:
|
|
|
86baa9 |
+ # For a principal that must exist, we re-raise the exception
|
|
|
86baa9 |
+ # to let the caller to handle this situation
|
|
|
86baa9 |
+ if flags & self.KRB_PRINC_MUST_EXIST:
|
|
|
86baa9 |
+ raise
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ dn = DN(('krbprincipalname', trust_principal), trustdn)
|
|
|
86baa9 |
+ entry = ldap.make_entry(dn)
|
|
|
86baa9 |
+ logger.debug("Adding Kerberos principal entry for %s",
|
|
|
86baa9 |
+ trust_principal)
|
|
|
86baa9 |
+ action = ldap.add_entry
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ entry_data = {
|
|
|
86baa9 |
+ 'objectclass':
|
|
|
86baa9 |
+ ['krbPrincipal', 'krbPrincipalAux',
|
|
|
86baa9 |
+ 'krbTicketPolicyAux', 'top'],
|
|
|
86baa9 |
+ 'krbcanonicalname': [trust_principal],
|
|
|
86baa9 |
+ 'krbprincipalname': [trust_principal],
|
|
|
86baa9 |
+ }
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ entry_data['krbprincipalname'].extend(aliases)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ if flags & self.KRB_PRINC_CREATE_DISABLED:
|
|
|
86baa9 |
+ flg = int(entry.single_value.get('krbticketflags', 0))
|
|
|
86baa9 |
+ entry_data['krbticketflags'] = flg | self.KRB_DISALLOW_ALL_TIX
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ if flags & self.KRB_PRINC_CREATE_AGENT_PERMISSION:
|
|
|
86baa9 |
+ entry_data['objectclass'].extend(['ipaAllowedOperations'])
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ entry.update(entry_data)
|
|
|
86baa9 |
+ try:
|
|
|
86baa9 |
+ action(entry)
|
|
|
86baa9 |
+ except errors.EmptyModlist:
|
|
|
86baa9 |
+ logger.debug("No update was required for Kerberos principal %s",
|
|
|
86baa9 |
+ trust_principal)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # If entry existed, no need to set Kerberos keys on it
|
|
|
86baa9 |
+ if action == ldap.update_entry:
|
|
|
86baa9 |
+ logger.debug("No need to update Kerberos keys for "
|
|
|
86baa9 |
+ "existing Kerberos principal %s",
|
|
|
86baa9 |
+ trust_principal)
|
|
|
86baa9 |
+ return
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # Now that entry is updated, set its Kerberos keys.
|
|
|
86baa9 |
+ #
|
|
|
86baa9 |
+ # It would be a complication to use ipa-getkeytab LDAP extended control
|
|
|
86baa9 |
+ # here because we would need to encode the request in ASN.1 sequence
|
|
|
86baa9 |
+ # and we don't have the code to do so exposed in Python bindings.
|
|
|
86baa9 |
+ # Instead, as we run on IPA master, we can use kadmin.local for that
|
|
|
86baa9 |
+ # directly.
|
|
|
86baa9 |
+ # We pass the command as a stdin to both avoid shell interpolation
|
|
|
86baa9 |
+ # of the passwords and also to avoid its exposure to other processes
|
|
|
86baa9 |
+ # Since we don't want to record the output, make also a redacted log
|
|
|
86baa9 |
+ change_password = self.change_password_template.format(
|
|
|
86baa9 |
+ password=password,
|
|
|
86baa9 |
+ principal=trust_principal)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ redacted = self.change_password_template.format(
|
|
|
86baa9 |
+ password='<REDACTED OUT>',
|
|
|
86baa9 |
+ principal=trust_principal)
|
|
|
86baa9 |
+ logger.debug("Updating Kerberos keys for %s with the following "
|
|
|
86baa9 |
+ "kadmin command:\n\t%s", trust_principal, redacted)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ ipautil.run([paths.KADMIN_LOCAL, "-x",
|
|
|
86baa9 |
+ "ipa-setup-override-restrictions"],
|
|
|
86baa9 |
+ stdin=change_password, skip_output=True)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ def execute(self, **options):
|
|
|
86baa9 |
+ # First, see if trusts are enabled on the server
|
|
|
86baa9 |
+ if not self.api.Command.adtrust_is_enabled()['result']:
|
|
|
86baa9 |
+ logger.debug('AD Trusts are not enabled on this server')
|
|
|
86baa9 |
+ return False, []
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ ldap = self.api.Backend.ldap2
|
|
|
86baa9 |
+ gidNumber = get_gidNumber(ldap, self.api.env)
|
|
|
86baa9 |
+ if gidNumber is None:
|
|
|
86baa9 |
+ return False, []
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ result = self.api.Command.trustconfig_show()['result']
|
|
|
86baa9 |
+ our_nbt_name = result.get('ipantflatname', [None])[0]
|
|
|
86baa9 |
+ if not our_nbt_name:
|
|
|
86baa9 |
+ return False, []
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ trusts_dn = self.api.env.container_adtrusts + self.api.env.basedn
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ trusts = ldap.get_entries(
|
|
|
86baa9 |
+ base_dn=trusts_dn,
|
|
|
86baa9 |
+ scope=ldap.SCOPE_ONELEVEL,
|
|
|
86baa9 |
+ filter=self.trust_filter,
|
|
|
86baa9 |
+ attrs_list=self.trust_attrs)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # For every trust, retrieve its principals and convert
|
|
|
86baa9 |
+ for t_entry in trusts:
|
|
|
86baa9 |
+ t_dn = t_entry.dn
|
|
|
86baa9 |
+ logger.debug('Processing trust domain object %s', str(t_dn))
|
|
|
86baa9 |
+ t_realm = t_entry.single_value.get('ipaNTTrustPartner').upper()
|
|
|
86baa9 |
+ direction = int(t_entry.single_value.get('ipaNTTrustDirection'))
|
|
|
86baa9 |
+ passwd_incoming = self.retrieve_trust_password(
|
|
|
86baa9 |
+ ndr_unpack(drsblobs.trustAuthInOutBlob,
|
|
|
86baa9 |
+ t_entry.single_value.get('ipaNTTrustAuthIncoming')))
|
|
|
86baa9 |
+ passwd_outgoing = self.retrieve_trust_password(
|
|
|
86baa9 |
+ ndr_unpack(drsblobs.trustAuthInOutBlob,
|
|
|
86baa9 |
+ t_entry.single_value.get('ipaNTTrustAuthOutgoing')))
|
|
|
86baa9 |
+ # For outbound and inbound trusts, process four principals total
|
|
|
86baa9 |
+ if (direction & TRUST_BIDIRECTIONAL) == TRUST_BIDIRECTIONAL:
|
|
|
86baa9 |
+ # 1. OUTBOUND: krbtgt/<REMOTE REALM>@<OUR REALM> must exist
|
|
|
86baa9 |
+ trust_principal = self.tgt_principal_template.format(
|
|
|
86baa9 |
+ remote=t_realm, local=self.api.env.realm)
|
|
|
86baa9 |
+ try:
|
|
|
86baa9 |
+ self.set_krb_principal(trust_principal,
|
|
|
86baa9 |
+ passwd_outgoing,
|
|
|
86baa9 |
+ t_dn,
|
|
|
86baa9 |
+ flags=self.KRB_PRINC_CREATE_DEFAULT)
|
|
|
86baa9 |
+ except errors.NotFound:
|
|
|
86baa9 |
+ # It makes no sense to convert this one, skip the trust
|
|
|
86baa9 |
+ # completely, better to re-establish one
|
|
|
86baa9 |
+ logger.error(
|
|
|
86baa9 |
+ "Broken trust to AD: %s not found, "
|
|
|
86baa9 |
+ "please re-establish the trust to %s",
|
|
|
86baa9 |
+ trust_principal, t_realm)
|
|
|
86baa9 |
+ continue
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # 2. Create <REMOTE FLATNAME$>@<OUR REALM>
|
|
|
86baa9 |
+ nbt_name = t_entry.single_value.get('ipaNTFlatName')
|
|
|
86baa9 |
+ nbt_principal = self.nbt_principal_template.format(
|
|
|
86baa9 |
+ nbt=nbt_name, realm=self.api.env.realm)
|
|
|
86baa9 |
+ tgt_principal = self.tgt_principal_template.format(
|
|
|
86baa9 |
+ remote=nbt_name, local=self.api.env.realm)
|
|
|
86baa9 |
+ self.set_krb_principal([nbt_principal, tgt_principal],
|
|
|
86baa9 |
+ passwd_incoming,
|
|
|
86baa9 |
+ t_dn,
|
|
|
86baa9 |
+ flags=self.KRB_PRINC_CREATE_DEFAULT)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # 3. INBOUND: krbtgt/<OUR REALM>@<REMOTE REALM> must exist
|
|
|
86baa9 |
+ trust_principal = self.tgt_principal_template.format(
|
|
|
86baa9 |
+ remote=self.api.env.realm, local=t_realm)
|
|
|
86baa9 |
+ try:
|
|
|
86baa9 |
+ self.set_krb_principal(trust_principal, passwd_outgoing,
|
|
|
86baa9 |
+ t_dn,
|
|
|
86baa9 |
+ flags=self.KRB_PRINC_CREATE_DEFAULT)
|
|
|
86baa9 |
+ except errors.NotFound:
|
|
|
86baa9 |
+ # It makes no sense to convert this one, skip the trust
|
|
|
86baa9 |
+ # completely, better to re-establish one
|
|
|
86baa9 |
+ logger.error(
|
|
|
86baa9 |
+ "Broken trust to AD: %s not found, "
|
|
|
86baa9 |
+ "please re-establish the trust to %s",
|
|
|
86baa9 |
+ trust_principal, t_realm)
|
|
|
86baa9 |
+ continue
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ # 4. Create <OUR FLATNAME$>@<REMOTE REALM>, disabled
|
|
|
86baa9 |
+ nbt_principal = self.nbt_principal_template.format(
|
|
|
86baa9 |
+ nbt=our_nbt_name, realm=t_realm)
|
|
|
86baa9 |
+ tgt_principal = self.tgt_principal_template.format(
|
|
|
86baa9 |
+ remote=our_nbt_name, local=t_realm)
|
|
|
86baa9 |
+ self.set_krb_principal([nbt_principal, tgt_principal],
|
|
|
86baa9 |
+ passwd_incoming,
|
|
|
86baa9 |
+ t_dn,
|
|
|
86baa9 |
+ flags=self.KRB_PRINC_CREATE_DEFAULT |
|
|
|
86baa9 |
+ self.KRB_PRINC_CREATE_AGENT_PERMISSION |
|
|
|
86baa9 |
+ self.KRB_PRINC_CREATE_DISABLED)
|
|
|
86baa9 |
+
|
|
|
86baa9 |
+ return False, []
|
|
|
86baa9 |
--
|
|
|
86baa9 |
2.20.1
|
|
|
86baa9 |
|