mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
trust: support retrieving POSIX IDs with one-way trust during trust-add
With one-way trust we cannot rely on cross-realm TGT as there will be none. Thus, if we have AD administrator credentials we should reuse them. Additionally, such use should be done over Kerberos. Fixes: https://fedorahosted.org/freeipa/ticket/4960 https://fedorahosted.org/freeipa/ticket/4959 Reviewed-By: Tomas Babej <tbabej@redhat.com>
This commit is contained in:
committed by
Tomas Babej
parent
5025204175
commit
2dd5b46d25
@@ -186,7 +186,9 @@ if domains:
|
||||
if idrange_type != u'ipa-ad-trust-posix':
|
||||
range_name = name.upper() + '_id_range'
|
||||
dom['range_type'] = u'ipa-ad-trust'
|
||||
trust.add_range(range_name, dom['ipanttrusteddomainsid'],
|
||||
# Do not pass ipaserver.dcerpc.TrustInstance to trust.add_range
|
||||
# to force it using existing credentials cache
|
||||
trust.add_range(None, range_name, dom['ipanttrusteddomainsid'],
|
||||
trusted_domain, name, **dom)
|
||||
except errors.DuplicateEntry:
|
||||
# Ignore updating duplicate entries
|
||||
|
||||
@@ -166,6 +166,9 @@ DEFAULT_RANGE_SIZE = 200000
|
||||
|
||||
DBUS_IFACE_TRUST = 'com.redhat.idm.trust'
|
||||
|
||||
CRED_STYLE_SAMBA = 1
|
||||
CRED_STYLE_KERBEROS = 2
|
||||
|
||||
def trust_type_string(level):
|
||||
"""
|
||||
Returns a string representing a type of the trust. The original field is an enum:
|
||||
@@ -196,7 +199,44 @@ def make_trust_dn(env, trust_type, dn):
|
||||
return DN(dn, container_dn)
|
||||
return dn
|
||||
|
||||
def add_range(myapi, range_name, dom_sid, *keys, **options):
|
||||
def generate_creds(trustinstance, style, **options):
|
||||
"""
|
||||
Generate string representing credentials using trust instance
|
||||
Input:
|
||||
trustinstance -- ipaserver.dcerpc.TrustInstance object
|
||||
style -- style of credentials
|
||||
CRED_STYLE_SAMBA -- for using with Samba bindings
|
||||
CRED_STYLE_KERBEROS -- for obtaining Kerberos ticket
|
||||
**options -- options with realm_admin and realm_passwd keys
|
||||
|
||||
Result:
|
||||
a string representing credentials with first % separating username and password
|
||||
None is returned if realm_passwd key returns nothing from options
|
||||
"""
|
||||
creds = None
|
||||
password = options.get('realm_passwd', None)
|
||||
if password:
|
||||
admin_name = options.get('realm_admin')
|
||||
sp = []
|
||||
sep = '@'
|
||||
if style == CRED_STYLE_SAMBA:
|
||||
sep = "\\"
|
||||
sp = admin_name.split(sep)
|
||||
if len(sp) == 1:
|
||||
sp.insert(0, trustinstance.remote_domain.info['name'])
|
||||
elif style == CRED_STYLE_KERBEROS:
|
||||
sp = admin_name.split('\\')
|
||||
if len(sp) > 1:
|
||||
sp = [sp[1]]
|
||||
else:
|
||||
sp = admin_name.split(sep)
|
||||
if len(sp) == 1:
|
||||
sp.append(trustinstance.remote_domain.info['dns_forest'].upper())
|
||||
creds = u"{name}%{password}".format(name=sep.join(sp),
|
||||
password=password)
|
||||
return creds
|
||||
|
||||
def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options):
|
||||
"""
|
||||
First, we try to derive the parameters of the ID range based on the
|
||||
information contained in the Active Directory.
|
||||
@@ -236,6 +276,12 @@ def add_range(myapi, range_name, dom_sid, *keys, **options):
|
||||
'domain configured. Make sure you have run '
|
||||
'ipa-adtrust-install on the IPA server first'))
|
||||
|
||||
creds = None
|
||||
if trustinstance:
|
||||
# Re-use AD administrator credentials if they were provided
|
||||
creds = generate_creds(trustinstance, style=CRED_STYLE_KERBEROS, **options)
|
||||
if creds:
|
||||
domain_validator._admin_creds = creds
|
||||
# KDC might not get refreshed data at the first time,
|
||||
# retry several times
|
||||
for retry in range(10):
|
||||
@@ -516,7 +562,8 @@ sides.
|
||||
# Store the created range type, since for POSIX trusts no
|
||||
# ranges for the subdomains should be added, POSIX attributes
|
||||
# provide a global mapping across all subdomains
|
||||
(created_range_type, _, _) = add_range(self.api, range_name, dom_sid,
|
||||
(created_range_type, _, _) = add_range(self.api, self.trustinstance,
|
||||
range_name, dom_sid,
|
||||
*keys, **options)
|
||||
else:
|
||||
created_range_type = old_range['result']['iparangetype'][0]
|
||||
@@ -1348,19 +1395,9 @@ class trustdomain_del(LDAPDelete):
|
||||
return result
|
||||
|
||||
|
||||
|
||||
|
||||
def fetch_domains_from_trust(myapi, trustinstance, trust_entry, **options):
|
||||
trust_name = trust_entry['cn'][0]
|
||||
creds = None
|
||||
password = options.get('realm_passwd', None)
|
||||
if password:
|
||||
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)
|
||||
creds = generate_creds(trustinstance, style=CRED_STYLE_SAMBA, **options)
|
||||
server = options.get('realm_server', None)
|
||||
domains = ipaserver.dcerpc.fetch_domains(myapi,
|
||||
trustinstance.local_flatname,
|
||||
@@ -1394,7 +1431,7 @@ def add_new_domains_from_trust(myapi, trustinstance, trust_entry, domains, **opt
|
||||
if idrange_type != u'ipa-ad-trust-posix':
|
||||
range_name = name.upper() + '_id_range'
|
||||
dom['range_type'] = u'ipa-ad-trust'
|
||||
add_range(myapi, range_name, dom['ipanttrusteddomainsid'],
|
||||
add_range(myapi, trustinstance, range_name, dom['ipanttrusteddomainsid'],
|
||||
trust_name, name, **dom)
|
||||
except errors.DuplicateEntry:
|
||||
# Ignore updating duplicate entries
|
||||
|
||||
@@ -151,6 +151,7 @@ class DomainValidator(object):
|
||||
self._domains = None
|
||||
self._info = dict()
|
||||
self._creds = None
|
||||
self._admin_creds = None
|
||||
self._parm = None
|
||||
|
||||
def is_configured(self):
|
||||
@@ -565,6 +566,52 @@ class DomainValidator(object):
|
||||
% (stdout, stderr))
|
||||
return (None, None)
|
||||
|
||||
def kinit_as_administrator(self, domain):
|
||||
"""
|
||||
Initializes ccache with http service credentials.
|
||||
|
||||
Applies session code defaults for ccache directory and naming prefix.
|
||||
Session code uses krbccache_prefix+<pid>, we use
|
||||
krbccache_prefix+<TD>+<domain netbios name> so there is no clash.
|
||||
|
||||
Returns tuple (ccache path, principal) where (None, None) signifes an
|
||||
error on ccache initialization
|
||||
"""
|
||||
|
||||
if self._admin_creds == None:
|
||||
return (None, None)
|
||||
|
||||
domain_suffix = domain.replace('.', '-')
|
||||
|
||||
ccache_name = "%sTDA%s" % (krbccache_prefix, domain_suffix)
|
||||
ccache_path = os.path.join(krbccache_dir, ccache_name)
|
||||
|
||||
(principal, password) = self._admin_creds.split('%', 1)
|
||||
|
||||
# Destroy the contents of the ccache
|
||||
root_logger.debug('Destroying the contents of the separate ccache')
|
||||
|
||||
(stdout, stderr, returncode) = ipautil.run(
|
||||
[paths.KDESTROY, '-A', '-c', ccache_path],
|
||||
env={'KRB5CCNAME': ccache_path},
|
||||
raiseonerr=False)
|
||||
|
||||
# Destroy the contents of the ccache
|
||||
root_logger.debug('Running kinit with credentials of AD administrator')
|
||||
|
||||
(stdout, stderr, returncode) = ipautil.run(
|
||||
[paths.KINIT, principal],
|
||||
env={'KRB5CCNAME': ccache_path},
|
||||
stdin=password,
|
||||
raiseonerr=False)
|
||||
|
||||
if returncode == 0:
|
||||
return (ccache_path, principal)
|
||||
else:
|
||||
root_logger.debug('Kinit failed, stout: %s, stderr: %s'
|
||||
% (stdout, stderr))
|
||||
return (None, None)
|
||||
|
||||
def search_in_dc(self, domain, filter, attrs, scope, basedn=None,
|
||||
quiet=False):
|
||||
"""
|
||||
@@ -597,7 +644,8 @@ class DomainValidator(object):
|
||||
Returns LDAP result or None.
|
||||
"""
|
||||
|
||||
(ccache_name, principal) = self.kinit_as_http(info['dns_domain'])
|
||||
if self._admin_creds:
|
||||
(ccache_name, principal) = self.kinit_as_administrator(info['dns_domain'])
|
||||
|
||||
if ccache_name:
|
||||
with ipautil.private_ccache(path=ccache_name):
|
||||
@@ -1106,18 +1154,7 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
raise assess_dcerpc_exception(message=str(e))
|
||||
|
||||
td.info['dc'] = unicode(result.pdc_dns_name)
|
||||
if creds is None:
|
||||
# Attempt to authenticate as HTTP/ipa.master and use cross-forest trust
|
||||
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 ipautil.private_ccache(path=ccache_name):
|
||||
td.creds.guess(td.parm)
|
||||
td.creds.set_workstation(domain_validator.flatname)
|
||||
domains = communicate(td)
|
||||
elif type(creds) is bool:
|
||||
if type(creds) is bool:
|
||||
# Rely on existing Kerberos credentials in the environment
|
||||
td.creds = credentials.Credentials()
|
||||
td.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS)
|
||||
@@ -1125,13 +1162,23 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
td.creds.set_workstation(domain_validator.flatname)
|
||||
domains = communicate(td)
|
||||
else:
|
||||
# Assume we've got credentials as a string user%password
|
||||
# Attempt to authenticate as HTTP/ipa.master and use cross-forest trust
|
||||
# or as passed-in user in case of a one-way trust
|
||||
domval = DomainValidator(api)
|
||||
ccache_name = None
|
||||
principal = None
|
||||
if creds:
|
||||
domval._admin_creds = creds
|
||||
(ccache_name, principal) = domval.kinit_as_administrator(trustdomain)
|
||||
else:
|
||||
(ccache_name, principal) = domval.kinit_as_http(trustdomain)
|
||||
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)
|
||||
td.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS)
|
||||
if ccache_name:
|
||||
with ipautil.private_ccache(path=ccache_name):
|
||||
td.creds.guess(td.parm)
|
||||
td.creds.set_workstation(domain_validator.flatname)
|
||||
domains = communicate(td)
|
||||
|
||||
if domains is None:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user