summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2015-07-06 14:46:24 +0000
committerAlexander Bokovoy <abokovoy@redhat.com>2015-07-07 11:09:03 +0300
commit52e2ec266a293891819682487e37644ffcf11e4a (patch)
tree8e48f121448c8d21a543c34d8d52dcee9f73035e
parenta985b1792325e24584b2a0af27d88a494ef9c513 (diff)
downloadfreeipa-52e2ec266a293891819682487e37644ffcf11e4a.tar.gz
freeipa-52e2ec266a293891819682487e37644ffcf11e4a.tar.xz
freeipa-52e2ec266a293891819682487e37644ffcf11e4a.zip
trust: support retrieving POSIX IDs with one-way trust during trust-addoneway-trust
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
-rwxr-xr-xinstall/oddjob/com.redhat.idm.trust-fetch-domains4
-rw-r--r--ipalib/plugins/trust.py65
-rw-r--r--ipaserver/dcerpc.py83
3 files changed, 119 insertions, 33 deletions
diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains
index 2571dd09a..85e3cc993 100755
--- a/install/oddjob/com.redhat.idm.trust-fetch-domains
+++ b/install/oddjob/com.redhat.idm.trust-fetch-domains
@@ -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
diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py
index 9fbaf2507..196df5926 100644
--- a/ipalib/plugins/trust.py
+++ b/ipalib/plugins/trust.py
@@ -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
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index b11233d8d..bc75a6026 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -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):
@@ -1104,10 +1152,24 @@ 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:
+ 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)
+ td.creds.guess(td.parm)
+ td.creds.set_workstation(domain_validator.flatname)
+ domains = communicate(td)
+ else:
# 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, principal) = domval.kinit_as_http(trustdomain)
+ 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.MUST_USE_KERBEROS)
if ccache_name:
@@ -1115,21 +1177,6 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
td.creds.guess(td.parm)
td.creds.set_workstation(domain_validator.flatname)
domains = communicate(td)
- elif 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)
- td.creds.guess(td.parm)
- td.creds.set_workstation(domain_validator.flatname)
- domains = communicate(td)
- else:
- # Assume we've got credentials as a string user%password
- 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:
return None