summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--API.txt3
-rw-r--r--ipaserver/dcerpc.py50
-rw-r--r--ipaserver/plugins/trust.py61
3 files changed, 86 insertions, 28 deletions
diff --git a/API.txt b/API.txt
index f17093022..d5fbc2750 100644
--- a/API.txt
+++ b/API.txt
@@ -5208,12 +5208,13 @@ arg: Str('cn', cli_name='name')
option: Str('version?')
output: Output('result')
command: trust_add
-args: 1,14,3
+args: 1,15,3
arg: Str('cn', cli_name='realm')
option: Str('addattr*', cli_name='addattr')
option: Flag('all', autofill=True, cli_name='all', default=False)
option: Int('base_id?', cli_name='base_id')
option: Bool('bidirectional?', cli_name='two_way', default=False)
+option: Bool('external?', cli_name='external', default=False)
option: Int('range_size?', cli_name='range_size')
option: StrEnum('range_type?', cli_name='range_type', values=[u'ipa-ad-trust-posix', u'ipa-ad-trust'])
option: Flag('raw', autofill=True, cli_name='raw', default=False)
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 25ffbfaea..e1921dc0c 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -77,6 +77,11 @@ and Samba4 python bindings.
TRUST_ONEWAY = 1
TRUST_BIDIRECTIONAL = 3
+# Trust join behavior
+# External trust -- allow creating trust to a non-root domain in the forest
+TRUST_JOIN_EXTERNAL = 1
+
+
def is_sid_valid(sid):
try:
security.dom_sid(sid)
@@ -1037,7 +1042,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, trust_type='bidirectional'):
+ def establish_trust(self, another_domain, trustdom_secret, trust_type='bidirectional', trust_external=False):
"""
Establishes trust between our and another domain
Input: another_domain -- instance of TrustDomainInstance, initialized with #retrieve call
@@ -1060,6 +1065,8 @@ class TrustDomainInstance(object):
info.trust_direction |= lsa.LSA_TRUST_DIRECTION_OUTBOUND
info.trust_type = lsa.LSA_TRUST_TYPE_UPLEVEL
info.trust_attributes = 0
+ if trust_external:
+ info.trust_attributes |= lsa.LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
try:
dname = lsa.String()
@@ -1096,14 +1103,17 @@ class TrustDomainInstance(object):
# server as that one doesn't support AES encryption types
pass
- try:
- 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 as e:
- root_logger.error('unable to set trust to transitive: %s' % (str(e)))
+ if not trust_external:
+ try:
+ 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 as e:
+ root_logger.error('unable to set trust transitivity status: %s' % (str(e)))
- if self.info['is_pdc']:
+ if self.info['is_pdc'] or trust_external:
self.update_ftinfo(another_domain)
def verify_trust(self, another_domain):
@@ -1262,6 +1272,7 @@ class TrustDomainJoins(object):
self.api = api
self.local_domain = None
self.remote_domain = None
+ self.__allow_behavior = 0
domain_validator = DomainValidator(api)
self.configured = domain_validator.is_configured()
@@ -1271,6 +1282,10 @@ class TrustDomainJoins(object):
self.local_dn = domain_validator.dn
self.__populate_local_domain()
+ def allow_behavior(self, *flags):
+ for f in flags:
+ self.__allow_behavior |= int(f)
+
def __populate_local_domain(self):
# Initialize local domain info using kerberos only
ld = TrustDomainInstance(self.local_flatname)
@@ -1358,14 +1373,19 @@ class TrustDomainJoins(object):
realm_passwd
)
+ trust_external = bool(self.__allow_behavior & TRUST_JOIN_EXTERNAL)
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'])
+ if not trust_external:
+ raise errors.NotAForestRootError(forest=self.remote_domain.info['dns_forest'],
+ domain=self.remote_domain.info['dns_domain'])
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, trust_type)
- self.local_domain.establish_trust(self.remote_domain, trustdom_pass, trust_type)
+ self.remote_domain.establish_trust(self.local_domain,
+ trustdom_pass, trust_type, trust_external)
+ self.local_domain.establish_trust(self.remote_domain,
+ trustdom_pass, trust_type, trust_external)
# 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
@@ -1381,8 +1401,12 @@ class TrustDomainJoins(object):
if not(isinstance(self.remote_domain, TrustDomainInstance)):
self.populate_remote_domain(realm, realm_server, realm_passwd=None)
+ trust_external = bool(self.__allow_behavior & TRUST_JOIN_EXTERNAL)
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'])
+ if not trust_external:
+ 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, trust_type)
+ self.local_domain.establish_trust(self.remote_domain,
+ trustdom_passwd, trust_type, trust_external)
return dict(local=self.local_domain, remote=self.remote_domain, verified=False)
diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py
index ee0ab5d10..744be93c7 100644
--- a/ipaserver/plugins/trust.py
+++ b/ipaserver/plugins/trust.py
@@ -62,8 +62,10 @@ except Exception as e:
if api.env.in_server and api.env.context in ['lite', 'server']:
try:
- import ipaserver.dcerpc #pylint: disable=F0401
- from ipaserver.dcerpc import TRUST_ONEWAY, TRUST_BIDIRECTIONAL
+ import ipaserver.dcerpc # pylint: disable=F0401
+ from ipaserver.dcerpc import (TRUST_ONEWAY,
+ TRUST_BIDIRECTIONAL,
+ TRUST_JOIN_EXTERNAL)
import dbus
import dbus.mainloop.glib
_bindings_installed = True
@@ -162,11 +164,18 @@ trust_output_params = (
label=_('Trust type')),
Str('truststatus',
label=_('Trust status')),
+ Str('ipantadditionalsuffixes*',
+ label=_('UPN suffixes')),
)
+# Trust type is a combination of ipanttrusttype and ipanttrustattributes
+# We shift trust attributes by 3 bits to left so bit 0 becomes bit 3 and
+# 2+(1 << 3) becomes 10.
_trust_type_dict = {1 : _('Non-Active Directory domain'),
2 : _('Active Directory domain'),
- 3 : _('RFC4120-compliant Kerberos realm')}
+ 3 : _('RFC4120-compliant Kerberos realm'),
+ 10: _('Non-transitive external trust to a domain in another Active Directory forest')}
+
_trust_direction_dict = {1 : _('Trusting forest'),
2 : _('Trusted forest'),
3 : _('Two-way trust')}
@@ -189,14 +198,17 @@ DBUS_IFACE_TRUST = 'com.redhat.idm.trust'
CRED_STYLE_SAMBA = 1
CRED_STYLE_KERBEROS = 2
-def trust_type_string(level):
+LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE = 0x00000001
+
+def trust_type_string(level, attrs):
"""
Returns a string representing a type of the trust. The original field is an enum:
LSA_TRUST_TYPE_DOWNLEVEL = 0x00000001,
LSA_TRUST_TYPE_UPLEVEL = 0x00000002,
LSA_TRUST_TYPE_MIT = 0x00000003
"""
- string = _trust_type_dict.get(int(level), _trust_type_dict_unknown)
+ transitive = int(attrs) & LSA_TRUST_ATTRIBUTE_NON_TRANSITIVE
+ string = _trust_type_dict.get(int(level) | (transitive << 3), _trust_type_dict_unknown)
return unicode(string)
def trust_direction_string(level):
@@ -677,6 +689,12 @@ sides.
doc=(_('Establish bi-directional trust. By default trust is inbound one-way only.')),
default=False,
),
+ Bool('external?',
+ label=_('External trust'),
+ cli_name='external',
+ doc=(_('Establish external trust to a domain in another forest. The trust is not transitive beyond the domain.')),
+ default=False,
+ ),
)
msg_summary = _('Added Active Directory trust for realm "%(value)s"')
@@ -735,12 +753,15 @@ sides.
fetch_trusted_domains_over_dbus(self.api, self.log, result['value'])
# Format the output into human-readable values
+ attributes = int(result['result'].get('ipanttrustattributes', [0])[0])
result['result']['trusttype'] = [trust_type_string(
- result['result']['ipanttrusttype'][0])]
+ result['result']['ipanttrusttype'][0], attributes)]
result['result']['trustdirection'] = [trust_direction_string(
result['result']['ipanttrustdirection'][0])]
result['result']['truststatus'] = [trust_status_string(
result['verified'])]
+ if attributes:
+ result['result'].pop('ipanttrustattributes', None)
del result['verified']
result['result'].pop('ipanttrustauthoutgoing', None)
@@ -929,6 +950,11 @@ sides.
trust_type = TRUST_ONEWAY
if options.get('bidirectional', False):
trust_type = TRUST_BIDIRECTIONAL
+
+ # If we are forced to establish external trust, allow it
+ if options.get('external', False):
+ self.trustinstance.allow_behavior(TRUST_JOIN_EXTERNAL)
+
# 1. Full access to the remote domain. Use admin credentials and
# generate random trustdom password to do work on both sides
if full_join:
@@ -1033,7 +1059,7 @@ class trust_mod(LDAPUpdate):
class trust_find(LDAPSearch):
__doc__ = _('Search for trusts.')
has_output_params = LDAPSearch.has_output_params + trust_output_params +\
- (Str('ipanttrusttype'),)
+ (Str('ipanttrusttype'), Str('ipanttrustattributes'))
msg_summary = ngettext(
'%(count)d trust matched', '%(count)d trusts matched', 0
@@ -1043,7 +1069,7 @@ class trust_find(LDAPSearch):
# search needs to be done on a sub-tree scope
def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
# list only trust, not trust domains
- trust_filter = '(ipaNTTrustPartner=*)'
+ trust_filter = '(&(ipaNTTrustPartner=*)(&(objectclass=ipaIDObject)(objectclass=ipaNTTrustedDomain)))'
filter = ldap.combine_filters((filters, trust_filter), rules=ldap.MATCH_ALL)
return (filter, base_dn, ldap.SCOPE_SUBTREE)
@@ -1060,10 +1086,13 @@ class trust_find(LDAPSearch):
for attrs in entries:
# Translate ipanttrusttype to trusttype if --raw not used
- trust_type = attrs.get('ipanttrusttype', [None])[0]
+ trust_type = attrs.single_value.get('ipanttrusttype', None)
+ attributes = attrs.single_value.get('ipanttrustattributes', 0)
if not options.get('raw', False) and trust_type is not None:
- attrs['trusttype'] = trust_type_string(attrs['ipanttrusttype'][0])
+ attrs['trusttype'] = [trust_type_string(trust_type, attributes)]
del attrs['ipanttrusttype']
+ if attributes:
+ del attrs['ipanttrustattributes']
return truncated
@@ -1071,7 +1100,7 @@ class trust_find(LDAPSearch):
class trust_show(LDAPRetrieve):
__doc__ = _('Display information about a trust.')
has_output_params = LDAPRetrieve.has_output_params + trust_output_params +\
- (Str('ipanttrusttype'), Str('ipanttrustdirection'))
+ (Str('ipanttrusttype'), Str('ipanttrustdirection'), Str('ipanttrustattributes'))
def execute(self, *keys, **options):
result = super(trust_show, self).execute(*keys, **options)
@@ -1088,16 +1117,20 @@ class trust_show(LDAPRetrieve):
# if --raw not used
if not options.get('raw', False):
- trust_type = entry_attrs.get('ipanttrusttype', [None])[0]
+ trust_type = entry_attrs.single_value.get('ipanttrusttype', None)
+ attributes = entry_attrs.single_value.get('ipanttrustattributes', 0)
if trust_type is not None:
- entry_attrs['trusttype'] = trust_type_string(trust_type)
+ entry_attrs['trusttype'] = [trust_type_string(trust_type, attributes)]
del entry_attrs['ipanttrusttype']
- dir_str = entry_attrs.get('ipanttrustdirection', [None])[0]
+ dir_str = entry_attrs.single_value.get('ipanttrustdirection', None)
if dir_str is not None:
entry_attrs['trustdirection'] = [trust_direction_string(dir_str)]
del entry_attrs['ipanttrustdirection']
+ if attributes:
+ del entry_attrs['ipanttrustattributes']
+
return dn