summaryrefslogtreecommitdiffstats
path: root/ipaserver
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2015-06-05 12:57:02 +0000
committerTomas Babej <tbabej@redhat.com>2015-07-08 01:56:52 +0200
commitd5aa1ee04e2e4923f42bccd60d51f063df144a0b (patch)
tree74df23dea20969b91cc881eb79ae13346c4012ae /ipaserver
parent14992a07fc7ea6bb5c028e5fefaf7394af00a555 (diff)
downloadfreeipa-d5aa1ee04e2e4923f42bccd60d51f063df144a0b.tar.gz
freeipa-d5aa1ee04e2e4923f42bccd60d51f063df144a0b.tar.xz
freeipa-d5aa1ee04e2e4923f42bccd60d51f063df144a0b.zip
trusts: add support for one-way trust and switch to it by default
One-way trust is the default now, use 'trust add --two-way ' to force bidirectional trust https://fedorahosted.org/freeipa/ticket/4959 In case of one-way trust we cannot authenticate using cross-realm TGT against an AD DC. We have to use trusted domain object from within AD domain and access to this object is limited to avoid compromising the whole trust configuration. Instead, IPA framework can call out to oddjob daemon and ask it to run the script which can have access to the TDO object. This script (com.redhat.idm.trust-fetch-domains) is using cifs/ipa.master principal to retrieve TDO object credentials from IPA LDAP if needed and then authenticate against AD DCs using the TDO object credentials. The script pulls the trust topology out of AD DCs and updates IPA LDAP store. Then IPA framework can pick the updated data from the IPA LDAP under normal access conditions. Part of https://fedorahosted.org/freeipa/ticket/4546 Reviewed-By: Tomas Babej <tbabej@redhat.com>
Diffstat (limited to 'ipaserver')
-rw-r--r--ipaserver/dcerpc.py44
1 files changed, 32 insertions, 12 deletions
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index a54f5f67f..bfdfe2c5d 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -66,6 +66,10 @@ The code in this module relies heavily on samba4-python package
and Samba4 python bindings.
""")
+# Both constants can be used as masks against trust direction
+# because bi-directional has two lower bits set.
+TRUST_ONEWAY = 1
+TRUST_BIDIRECTIONAL = 3
def is_sid_valid(sid):
try:
@@ -949,7 +953,7 @@ class TrustDomainInstance(object):
# We can ignore the error here -- setting up name suffix routes may fail
pass
- def establish_trust(self, another_domain, trustdom_secret):
+ def establish_trust(self, another_domain, trustdom_secret, trust_type='bidirectional'):
"""
Establishes trust between our and another domain
Input: another_domain -- instance of TrustDomainInstance, initialized with #retrieve call
@@ -967,7 +971,9 @@ class TrustDomainInstance(object):
info.domain_name.string = another_domain.info['dns_domain']
info.netbios_name.string = another_domain.info['name']
info.sid = security.dom_sid(another_domain.info['sid'])
- info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND | lsa.LSA_TRUST_DIRECTION_OUTBOUND
+ info.trust_direction = lsa.LSA_TRUST_DIRECTION_INBOUND
+ if trust_type == TRUST_BIDIRECTIONAL:
+ info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
info.trust_attributes = 0
@@ -1005,7 +1011,8 @@ class TrustDomainInstance(object):
pass
try:
- info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
+ info = self._pipe.QueryTrustedDomainInfo(trustdom_handle, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX)
+ info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE
self._pipe.SetInformationTrustedDomain(trustdom_handle, lsa.LSA_TRUSTED_DOMAIN_INFO_INFO_EX, info)
except RuntimeError, e:
root_logger.error('unable to set trust to transitive: %s' % (str(e)))
@@ -1014,10 +1021,10 @@ class TrustDomainInstance(object):
self.update_ftinfo(another_domain)
def verify_trust(self, another_domain):
- def retrieve_netlogon_info_2(domain, function_code, data):
+ def retrieve_netlogon_info_2(logon_server, domain, function_code, data):
try:
netr_pipe = netlogon.netlogon(domain.binding, domain.parm, domain.creds)
- result = netr_pipe.netr_LogonControl2Ex(logon_server=None,
+ result = netr_pipe.netr_LogonControl2Ex(logon_server=logon_server,
function_code=function_code,
level=2,
data=data
@@ -1026,7 +1033,7 @@ class TrustDomainInstance(object):
except RuntimeError, (num, message):
raise assess_dcerpc_exception(num=num, message=message)
- result = retrieve_netlogon_info_2(self,
+ result = retrieve_netlogon_info_2(None, self,
netlogon.NETLOGON_CONTROL_TC_VERIFY,
another_domain.info['dns_domain'])
if (result and (result.flags and netlogon.NETLOGON_VERIFY_STATUS_RETURNED)):
@@ -1100,6 +1107,7 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
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()
@@ -1109,7 +1117,15 @@ 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)
@@ -1222,7 +1238,7 @@ class TrustDomainJoins(object):
ftinfo['rec_type'] = lsa.LSA_FOREST_TRUST_TOP_LEVEL_NAME
self.local_domain.ftinfo_records.append(ftinfo)
- def join_ad_full_credentials(self, realm, realm_server, realm_admin, realm_passwd):
+ def join_ad_full_credentials(self, realm, realm_server, realm_admin, realm_passwd, trust_type):
if not self.configured:
return None
@@ -1240,13 +1256,17 @@ class TrustDomainJoins(object):
if not self.remote_domain.read_only:
trustdom_pass = samba.generate_random_password(128, 128)
self.get_realmdomains()
- self.remote_domain.establish_trust(self.local_domain, trustdom_pass)
- self.local_domain.establish_trust(self.remote_domain, trustdom_pass)
- result = self.remote_domain.verify_trust(self.local_domain)
+ self.remote_domain.establish_trust(self.local_domain, trustdom_pass, trust_type)
+ self.local_domain.establish_trust(self.remote_domain, trustdom_pass, trust_type)
+ # if trust is inbound, we don't need to verify it because AD DC will respond
+ # with WERR_NO_SUCH_DOMAIN -- in only does verification for outbound trusts.
+ result = True
+ if trust_type == TRUST_BIDIRECTIONAL:
+ result = self.remote_domain.verify_trust(self.local_domain)
return dict(local=self.local_domain, remote=self.remote_domain, verified=result)
return None
- def join_ad_ipa_half(self, realm, realm_server, trustdom_passwd):
+ def join_ad_ipa_half(self, realm, realm_server, trustdom_passwd, trust_type):
if not self.configured:
return None
@@ -1256,5 +1276,5 @@ class TrustDomainJoins(object):
if self.remote_domain.info['dns_domain'] != self.remote_domain.info['dns_forest']:
raise errors.NotAForestRootError(forest=self.remote_domain.info['dns_forest'], domain=self.remote_domain.info['dns_domain'])
- self.local_domain.establish_trust(self.remote_domain, trustdom_passwd)
+ self.local_domain.establish_trust(self.remote_domain, trustdom_passwd, trust_type)
return dict(local=self.local_domain, remote=self.remote_domain, verified=False)