diff options
-rw-r--r-- | API.txt | 88 | ||||
-rw-r--r-- | ipalib/plugins/trust.py | 329 | ||||
-rw-r--r-- | ipaserver/dcerpc.py | 54 |
3 files changed, 420 insertions, 51 deletions
@@ -3423,6 +3423,17 @@ option: Str('version?', exclude='webui') output: Output('result', <type 'dict'>, None) output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Output('value', <type 'unicode'>, None) +command: trust_fetch_domains +args: 1,4,4 +arg: Str('cn', attribute=True, cli_name='realm', multivalue=False, primary_key=True, query=True, required=True) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('version?', exclude='webui') +output: Output('count', <type 'int'>, None) +output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Output('truncated', <type 'bool'>, None) command: trust_find args: 1,11,4 arg: Str('criteria?', noextrawhitespace=False) @@ -3497,6 +3508,83 @@ option: Str('version?', exclude='webui') output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) output: Output('value', <type 'unicode'>, None) +command: trustdomain_add +args: 2,9,3 +arg: Str('trustcn', cli_name='trust', query=True, required=True) +arg: Str('cn', attribute=True, cli_name='domain', multivalue=False, primary_key=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('ipantflatname', attribute=True, cli_name='flat_name', multivalue=False, required=False) +option: Str('ipanttrusteddomainsid', attribute=True, cli_name='sid', multivalue=False, required=False) +option: Str('ipanttrustpartner', attribute=True, cli_name='ipanttrustpartner', multivalue=False, required=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: StrEnum('trust_type', autofill=True, cli_name='type', default=u'ad', values=(u'ad',)) +option: Str('version?', exclude='webui') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Output('value', <type 'unicode'>, None) +command: trustdomain_del +args: 2,2,3 +arg: Str('trustcn', cli_name='trust', query=True, required=True) +arg: Str('cn', attribute=True, cli_name='domain', multivalue=True, primary_key=True, query=True, required=True) +option: Flag('continue', autofill=True, cli_name='continue', default=False) +option: Str('version?', exclude='webui') +output: Output('result', <type 'dict'>, None) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Output('value', <type 'unicode'>, None) +command: trustdomain_disable +args: 2,1,3 +arg: Str('trustcn', cli_name='trust', query=True, required=True) +arg: Str('cn', attribute=True, cli_name='domain', multivalue=False, primary_key=True, query=True, required=True) +option: Str('version?', exclude='webui') +output: Output('result', <type 'bool'>, None) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Output('value', <type 'unicode'>, None) +command: trustdomain_enable +args: 2,1,3 +arg: Str('trustcn', cli_name='trust', query=True, required=True) +arg: Str('cn', attribute=True, cli_name='domain', multivalue=False, primary_key=True, query=True, required=True) +option: Str('version?', exclude='webui') +output: Output('result', <type 'bool'>, None) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Output('value', <type 'unicode'>, None) +command: trustdomain_find +args: 2,10,4 +arg: Str('trustcn', cli_name='trust', query=True, required=True) +arg: Str('criteria?', noextrawhitespace=False) +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('cn', attribute=True, autofill=False, cli_name='domain', multivalue=False, primary_key=True, query=True, required=False) +option: Str('ipantflatname', attribute=True, autofill=False, cli_name='flat_name', multivalue=False, query=True, required=False) +option: Str('ipanttrusteddomainsid', attribute=True, autofill=False, cli_name='sid', multivalue=False, query=True, required=False) +option: Str('ipanttrustpartner', attribute=True, autofill=False, cli_name='ipanttrustpartner', multivalue=False, query=True, required=False) +option: Flag('pkey_only?', autofill=True, default=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Int('sizelimit?', autofill=False, minvalue=0) +option: Int('timelimit?', autofill=False, minvalue=0) +option: Str('version?', exclude='webui') +output: Output('count', <type 'int'>, None) +output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None)) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Output('truncated', <type 'bool'>, None) +command: trustdomain_mod +args: 2,11,3 +arg: Str('trustcn', cli_name='trust', query=True, required=True) +arg: Str('cn', attribute=True, cli_name='domain', multivalue=False, primary_key=True, query=True, required=True) +option: Str('addattr*', cli_name='addattr', exclude='webui') +option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui') +option: Str('delattr*', cli_name='delattr', exclude='webui') +option: Str('ipantflatname', attribute=True, autofill=False, cli_name='flat_name', multivalue=False, required=False) +option: Str('ipanttrusteddomainsid', attribute=True, autofill=False, cli_name='sid', multivalue=False, required=False) +option: Str('ipanttrustpartner', attribute=True, autofill=False, cli_name='ipanttrustpartner', multivalue=False, required=False) +option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui') +option: Flag('rights', autofill=True, default=False) +option: Str('setattr*', cli_name='setattr', exclude='webui') +option: StrEnum('trust_type', autofill=True, cli_name='type', default=u'ad', values=(u'ad',)) +option: Str('version?', exclude='webui') +output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None)) +output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None) +output: Output('value', <type 'unicode'>, None) command: user_add args: 1,35,3 arg: Str('uid', attribute=True, cli_name='login', maxlength=255, multivalue=False, pattern='^[a-zA-Z0-9_.][a-zA-Z0-9_.-]{0,252}[a-zA-Z0-9_.$-]?$', primary_key=True, required=True) diff --git a/ipalib/plugins/trust.py b/ipalib/plugins/trust.py index 3d0502990..53dee918d 100644 --- a/ipalib/plugins/trust.py +++ b/ipalib/plugins/trust.py @@ -181,6 +181,13 @@ def trust_status_string(level): string = _trust_status_dict.get(level, _trust_type_dict_unknown) return unicode(string) +def make_trust_dn(env, trust_type, dn): + assert isinstance(dn, DN) + if trust_type: + container_dn = DN(('cn', trust_type), env.container_trusts, env.basedn) + return DN(dn, container_dn) + return dn + class trust(LDAPObject): """ Trust object. @@ -195,7 +202,8 @@ class trust(LDAPObject): 'ipantauthtrustoutgoing', 'ipanttrustauthincoming', 'ipanttrustforesttrustinfo', 'ipanttrustposixoffset', 'ipantsupportedencryptiontypes' ] search_display_attributes = ['cn', 'ipantflatname', - 'ipanttrusteddomainsid', 'ipanttrusttype' ] + 'ipanttrusteddomainsid', 'ipanttrusttype', + 'ipantsidblacklistincoming', 'ipantsidblacklistoutgoing' ] label = _('Trusts') label_singular = _('Trust') @@ -241,12 +249,26 @@ class trust(LDAPObject): raise errors.ValidationError(name=attr, error=_("invalid SID: %(value)s") % dict(value=value)) -def make_trust_dn(env, trust_type, dn): - assert isinstance(dn, DN) - if trust_type in trust.trust_types: - container_dn = DN(('cn', trust_type), env.container_trusts, env.basedn) - return DN(dn[0], container_dn) - return dn + def get_dn(self, *keys, **kwargs): + sdn = map(lambda x: ('cn', x), keys) + sdn.reverse() + trust_type = kwargs.get('trust_type') + if trust_type is None: + ldap = self.backend + filter = ldap.make_filter({'objectclass': ['ipaNTTrustedDomain'], 'cn': [keys[-1]]}) + filter = ldap.combine_filters((filter, "ipaNTSIDBlacklistIncoming=*"), rules=ldap.MATCH_ALL) + try: + result = ldap.get_entries(DN(self.container_dn, self.env.basedn), + ldap.SCOPE_SUBTREE, filter, ['']) + except errors.NotFound: + trust_type = u'ad' + else: + if len(result) > 1: + raise errors.OnlyOneValueAllowed(attr='trust domain') + return result[0].dn + + dn=make_trust_dn(self.env, trust_type, DN(*sdn)) + return dn class trust_add(LDAPCreate): __doc__ = _(''' @@ -589,10 +611,13 @@ sides. def execute_ad(self, full_join, *keys, **options): # Join domain using full credentials and with random trustdom # secret (will be generated by the join method) - try: - api.Command['trust_show'](keys[-1]) + + # First see if the trust is already in place + # Force retrieval of the trust object by not passing trust_type + dn = self.obj.get_dn(keys[-1]) + if dn: summary = _('Re-established trust to domain "%(value)s"') - except errors.NotFound: + else: summary = self.msg_summary # 1. Full access to the remote domain. Use admin credentials and @@ -665,14 +690,6 @@ class trust_del(LDAPDelete): msg_summary = _('Deleted trust "%(value)s"') - def pre_callback(self, ldap, dn, *keys, **options): - assert isinstance(dn, DN) - try: - result = self.api.Command.trust_show(keys[-1]) - except errors.NotFound, e: - self.obj.handle_not_found(*keys) - return result['result']['dn'] - class trust_mod(LDAPUpdate): __doc__ = _(""" Modify a trust (for future use). @@ -686,16 +703,10 @@ class trust_mod(LDAPUpdate): def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): assert isinstance(dn, DN) - result = None - try: - result = self.api.Command.trust_show(keys[-1]) - except errors.NotFound, e: - self.obj.handle_not_found(*keys) self.obj.validate_sid_blacklists(entry_attrs) - # TODO: we found the trust object, now modify it - return result['result']['dn'] + return dn class trust_find(LDAPSearch): __doc__ = _('Search for trusts.') @@ -709,8 +720,10 @@ class trust_find(LDAPSearch): # Since all trusts types are stored within separate containers under 'cn=trusts', # search needs to be done on a sub-tree scope def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options): - assert isinstance(base_dn, DN) - return (filters, base_dn, ldap.SCOPE_SUBTREE) + # list only trust, not trust domains + trust_filter = '(ipaNTSIDBlacklistIncoming=*)' + filter = ldap.combine_filters((filters, trust_filter), rules=ldap.MATCH_ALL) + return (filter, base_dn, ldap.SCOPE_SUBTREE) def post_callback(self, ldap, entries, truncated, *args, **options): if options.get('pkey_only', False): @@ -731,30 +744,6 @@ class trust_show(LDAPRetrieve): has_output_params = LDAPRetrieve.has_output_params + trust_output_params +\ (Str('ipanttrusttype'), Str('ipanttrustdirection')) - def execute(self, *keys, **options): - error = None - result = None - for trust_type in trust.trust_types: - options['trust_show_type'] = trust_type - try: - result = super(trust_show, self).execute(*keys, **options) - except errors.NotFound, e: - result = None - error = e - if result: - break - if error or not result: - self.obj.handle_not_found(*keys) - - return result - - def pre_callback(self, ldap, dn, entry_attrs, *keys, **options): - assert isinstance(dn, DN) - if 'trust_show_type' in options: - return make_trust_dn(self.env, options['trust_show_type'], dn) - - return dn - def post_callback(self, ldap, dn, entry_attrs, *keys, **options): # Translate ipanttrusttype to trusttype @@ -1091,3 +1080,241 @@ class sidgen_was_run(Command): return dict(result=True) api.register(sidgen_was_run) + +class trustdomain(LDAPObject): + """ + Object representing a domain of the AD trust. + """ + parent_object = 'trust' + trust_type_idx = {'2':u'ad'} + object_name = _('trust domain') + object_name_plural = _('trust domains') + object_class = ['ipaNTTrustedDomain'] + default_attributes = ['cn', 'ipantflatname', 'ipanttrusteddomainsid', 'ipanttrustpartner'] + search_display_attributes = ['cn', 'ipantflatname', 'ipanttrusteddomainsid', ] + + label = _('Trusted domains') + label_singular = _('Trusted domain') + + takes_params = ( + Str('cn', + label=_('Domain name'), + cli_name='domain', + primary_key=True + ), + Str('ipantflatname?', + cli_name='flat_name', + label=_('Domain NetBIOS name'), + ), + Str('ipanttrusteddomainsid?', + cli_name='sid', + label=_('Domain Security Identifier'), + ), + Str('ipanttrustpartner?', + label=_('Trusted domain partner'), + flags=['no_display', 'no_option'], + ), + ) + + # LDAPObject.get_dn() only passes all but last element of keys and no kwargs + # to the parent object's get_dn() no matter what you pass to it. Make own get_dn() + # as we really need all elements to construct proper dn. + def get_dn(self, *keys, **kwargs): + sdn = map(lambda x: ('cn', x), keys) + sdn.reverse() + trust_type = kwargs.get('trust_type') + if not trust_type: + trust_type=u'ad' + + dn=make_trust_dn(self.env, trust_type, DN(*sdn)) + return dn +api.register(trustdomain) + +class trustdomain_find(LDAPSearch): + __doc__ = _('Search domains of the trust') + + def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options): + return (filters, base_dn, ldap.SCOPE_SUBTREE) +api.register(trustdomain_find) + +class trustdomain_mod(LDAPUpdate): + __doc__ = _('Modify trustdomain of the trust') + + NO_CLI = True + takes_options = LDAPUpdate.takes_options + (_trust_type_option,) +api.register(trustdomain_mod) + +class trustdomain_add(LDAPCreate): + __doc__ = _('Allow access from the trusted domain') + NO_CLI = True + + takes_options = LDAPCreate.takes_options + (_trust_type_option,) + def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options): + if 'ipanttrustpartner' in options: + entry_attrs['ipanttrustpartner'] = [options['ipanttrustpartner']] + return dn +api.register(trustdomain_add) + +class trustdomain_del(LDAPDelete): + __doc__ = _('Remove infromation about the domain associated with the trust.') + + msg_summary = _('Removed information about the trusted domain "%(value)s"') + + def execute(self, *keys, **options): + # Note that pre-/post- callback handling for LDAPDelete is causing pre_callback + # to always receive empty keys. We need to catch the case when root domain is being deleted + + for domain in keys[1]: + if keys[0].lower() == domain: + raise errors.ValidationError(name='domain', + error=_("cannot delete root domain of the trust, use trust-del to delete the trust itself")) + try: + res = api.Command.trustdomain_enable(keys[0], domain) + except errors.AlreadyActive: + pass + result = super(trustdomain_del, self).execute(*keys, **options) + result['value'] = u','.join(keys[1]) + return result + + +api.register(trustdomain_del) + + +def fetch_domains_from_trust(self, trustinstance, trust_entry): + trust_name = trust_entry['cn'][0] + domains = ipaserver.dcerpc.fetch_domains(self.api, trustinstance.local_flatname, trust_name) + result = [] + if not domains: + return None + + for dom in domains: + dom['trust_type'] = u'ad' + try: + name = dom['cn'] + del dom['cn'] + if 'all' in options: + dom['all'] = options['all'] + if 'raw' in options: + dom['raw'] = options['raw'] + res = self.api.Command.trustdomain_add(trust_name, name, **dom) + result.append(res['result']) + except errors.DuplicateEntry: + # Ignore updating duplicate entries + pass + return result + +class trust_fetch_domains(LDAPRetrieve): + __doc__ = _('Refresh list of the domains associated with the trust') + + has_output = output.standard_list_of_entries + + def execute(self, *keys, **options): + if not _bindings_installed: + raise errors.NotFound( + name=_('AD Trust setup'), + reason=_( + 'Cannot perform join operation without Samba 4 support ' + 'installed. Make sure you have installed server-trust-ad ' + 'sub-package of IPA' + ) + ) + trust = self.api.Command.trust_show(keys[0], raw=True)['result'] + + trustinstance = ipaserver.dcerpc.TrustDomainJoins(self.api) + if not trustinstance.configured: + raise errors.NotFound( + name=_('AD Trust setup'), + reason=_( + 'Cannot perform join operation without own domain ' + 'configured. Make sure you have run ipa-adtrust-install ' + 'on the IPA server first' + ) + ) + domains = fetch_domains_from_trust(self, trustinstance, trust) + result = dict() + + if len(domains) > 0: + result['summary'] = unicode(_('List of trust domains successfully refreshed')) + else: + result['summary'] = unicode(_('No new trust domains were found')) + + result['result'] = domains + result['count'] = len(domains) + result['truncated'] = False + return result + +api.register(trust_fetch_domains) + +class trustdomain_enable(LDAPQuery): + __doc__ = _('Allow use of IPA resources by the domain of the trust') + + has_output = output.standard_value + msg_summary = _('Enabled trust domain "%(value)s"') + + def execute(self, *keys, **options): + ldap = self.api.Backend.ldap2 + + if keys[0].lower() == keys[1].lower(): + raise errors.ValidationError(name='domain', + error=_("Root domain of the trust is always enabled for the existing trust")) + try: + trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad') + trust_entry = ldap.get_entry(trust_dn) + except errors.NotFound: + self.api.Object[self.obj.parent_object].handle_not_found(keys[0]) + + dn = self.obj.get_dn(keys[0], keys[1], trust_type=u'ad') + try: + entry = ldap.get_entry(dn) + sid = entry['ipanttrusteddomainsid'][0] + if sid in trust_entry['ipantsidblacklistincoming']: + trust_entry['ipantsidblacklistincoming'].remove(sid) + ldap.update_entry(trust_entry) + else: + raise errors.AlreadyActive() + except errors.NotFound: + self.obj.handle_not_found(*keys) + + return dict( + result=True, + value=keys[1], + ) + +api.register(trustdomain_enable) + +class trustdomain_disable(LDAPQuery): + __doc__ = _('Disable use of IPA resources by the domain of the trust') + + has_output = output.standard_value + msg_summary = _('Disabled trust domain "%(value)s"') + + def execute(self, *keys, **options): + ldap = self.api.Backend.ldap2 + + if keys[0].lower() == keys[1].lower(): + raise errors.ValidationError(name='domain', + error=_("cannot disable root domain of the trust, use trust-del to delete the trust itself")) + try: + trust_dn = self.obj.get_dn(keys[0], trust_type=u'ad') + trust_entry = ldap.get_entry(trust_dn) + except errors.NotFound: + self.api.Object[self.obj.parent_object].handle_not_found(keys[0]) + + dn = self.obj.get_dn(keys[0], keys[1], trust_type=u'ad') + try: + entry = ldap.get_entry(dn) + sid = entry['ipanttrusteddomainsid'][0] + if not (sid in trust_entry['ipantsidblacklistincoming']): + trust_entry['ipantsidblacklistincoming'].append(sid) + ldap.update_entry(trust_entry) + else: + raise errors.AlreadyInactive() + except errors.NotFound: + self.obj.handle_not_found(*keys) + + return dict( + result=True, + value=keys[1], + ) + +api.register(trustdomain_disable) diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py index cc9e7be44..1c4f4a6ff 100644 --- a/ipaserver/dcerpc.py +++ b/ipaserver/dcerpc.py @@ -1002,6 +1002,60 @@ class TrustDomainInstance(object): return True return False +def fetch_domains(api, mydomain, trustdomain): + trust_flags = dict( + NETR_TRUST_FLAG_IN_FOREST = 0x00000001, + NETR_TRUST_FLAG_OUTBOUND = 0x00000002, + NETR_TRUST_FLAG_TREEROOT = 0x00000004, + NETR_TRUST_FLAG_PRIMARY = 0x00000008, + NETR_TRUST_FLAG_NATIVE = 0x00000010, + NETR_TRUST_FLAG_INBOUND = 0x00000020, + NETR_TRUST_FLAG_MIT_KRB5 = 0x00000080, + NETR_TRUST_FLAG_AES = 0x00000100) + + trust_attributes = dict( + NETR_TRUST_ATTRIBUTE_NON_TRANSITIVE = 0x00000001, + NETR_TRUST_ATTRIBUTE_UPLEVEL_ONLY = 0x00000002, + NETR_TRUST_ATTRIBUTE_QUARANTINED_DOMAIN = 0x00000004, + NETR_TRUST_ATTRIBUTE_FOREST_TRANSITIVE = 0x00000008, + NETR_TRUST_ATTRIBUTE_CROSS_ORGANIZATION = 0x00000010, + NETR_TRUST_ATTRIBUTE_WITHIN_FOREST = 0x00000020, + NETR_TRUST_ATTRIBUTE_TREAT_AS_EXTERNAL = 0x00000040) + + domval = DomainValidator(api) + (ccache_name, principal) = domval.kinit_as_http(trustdomain) + if ccache_name: + with installutils.private_ccache(path=ccache_name): + td = TrustDomainInstance('') + td.parm.set('workgroup', mydomain) + td.creds = credentials.Credentials() + td.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS) + td.creds.guess(td.parm) + netrc = net.Net(creds=td.creds, lp=td.parm) + try: + result = netrc.finddc(domain=trustdomain, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS) + except RuntimeError, e: + raise assess_dcerpc_exception(message=str(e)) + if not result: + return None + td.retrieve(unicode(result.pdc_dns_name)) + + netr_pipe = netlogon.netlogon(td.binding, td.parm, td.creds) + domains = netr_pipe.netr_DsrEnumerateDomainTrusts(td.binding, 1) + + result = [] + for t in domains.array: + if ((t.trust_attributes & trust_attributes['NETR_TRUST_ATTRIBUTE_WITHIN_FOREST']) and + (t.trust_flags & trust_flags['NETR_TRUST_FLAG_IN_FOREST'])): + res = dict() + res['cn'] = unicode(t.dns_name) + res['ipantflatname'] = unicode(t.netbios_name) + res['ipanttrusteddomainsid'] = unicode(t.sid) + res['ipanttrustpartner'] = res['cn'] + result.append(res) + return result + + class TrustDomainJoins(object): def __init__(self, api): self.api = api |