Add trust management for Active Directory trusts

This commit is contained in:
Alexander Bokovoy 2012-02-28 13:24:41 +02:00 committed by Martin Kosek
parent dd244c02dd
commit a7420c1e83
10 changed files with 673 additions and 8 deletions

57
API.txt
View File

@ -3085,6 +3085,63 @@ option: Str('version?', exclude='webui')
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('value', <type 'unicode'>, None)
command: trust_add_ad
args: 1,7,3
arg: Str('cn', attribute=True, cli_name='realm', multivalue=False, primary_key=True, required=True)
option: Str('realm_admin?', cli_name='admin')
option: Password('realm_passwd?', cli_name='password', confirm=False)
option: Str('realm_server?', cli_name='server')
option: Password('trust_secret?', cli_name='trust_secret', confirm=False)
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: Str('version?', exclude='webui')
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('value', <type 'unicode'>, None)
command: trust_del
args: 1,1,3
arg: Str('cn', attribute=True, cli_name='realm', multivalue=True, primary_key=True, query=True, required=True)
option: Flag('continue', autofill=True, cli_name='continue', default=False)
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Output('result', <type 'dict'>, None)
output: Output('value', <type 'unicode'>, None)
command: trust_find
args: 1,7,4
arg: Str('criteria?', noextrawhitespace=False)
option: Str('cn', attribute=True, autofill=False, cli_name='realm', multivalue=False, primary_key=True, query=True, required=False)
option: Int('timelimit?', autofill=False, minvalue=0)
option: Int('sizelimit?', autofill=False, minvalue=0)
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: Str('version?', exclude='webui')
option: Flag('pkey_only?', autofill=True, default=False)
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
output: Output('count', <type 'int'>, None)
output: Output('truncated', <type 'bool'>, None)
command: trust_mod
args: 1,7,3
arg: Str('cn', attribute=True, cli_name='realm', multivalue=False, primary_key=True, query=True, required=True)
option: Str('setattr*', cli_name='setattr', exclude='webui')
option: Str('addattr*', cli_name='addattr', exclude='webui')
option: Str('delattr*', cli_name='delattr', exclude='webui')
option: Flag('rights', autofill=True, default=False)
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: Str('version?', exclude='webui')
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('value', <type 'unicode'>, None)
command: trust_show
args: 1,4,3
arg: Str('cn', attribute=True, cli_name='realm', multivalue=False, primary_key=True, query=True, required=True)
option: Flag('rights', autofill=True, default=False)
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: Str('version?', exclude='webui')
output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
output: Output('value', <type 'unicode'>, None)
command: user_add
args: 1,34,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)

View File

@ -189,6 +189,19 @@ user, virtual machines, groups, authentication credentials), Policy
(configuration settings, access control information) and Audit (events,
logs, analysis thereof). This package provides SELinux rules for the
daemons included in freeipa-server
%package server-trust-ad
Summary: Virtual package to install packages required for Active Directory trusts
Group: System Environment/Base
Requires: %{name}-server = %version-%release
Requires: python-crypto
Requires: samba4-python
Requires: samba4
%description server-trust-ad
Cross-realm trusts with Active Directory in IPA require working Samba 4 installation.
This package is provided for convenience to install all required dependencies at once.
%endif
@ -279,7 +292,6 @@ user, virtual machines, groups, authentication credentials), Policy
logs, analysis thereof). If you are using IPA you need to install this
package.
%prep
%setup -n freeipa-%{version} -q
@ -633,6 +645,9 @@ fi
%doc COPYING README Contributors.txt
%{_usr}/share/selinux/targeted/ipa_httpd.pp
%{_usr}/share/selinux/targeted/ipa_dogtag.pp
%files server-trust-ad
%{_usr}/share/ipa/smb.conf.empty
%endif
%files client
@ -684,6 +699,10 @@ fi
%ghost %attr(0644,root,apache) %config(noreplace) %{_sysconfdir}/ipa/ca.crt
%changelog
* Tue May 29 2012 Alexander Bokovoy <abokovoy@redhat.com> - 2.99.0-30
- Add freeipa-server-trust-ad virtual package to capture all required dependencies
for Active Directory trust management
* Fri May 11 2012 Martin Kosek <mkosek@redhat.com> - 2.99.0-29
- Replace used DNS client library (acutil) with python-dns

View File

@ -33,6 +33,7 @@ app_DATA = \
krbrealm.con.template \
preferences.html.template \
smb.conf.template \
smb.conf.empty \
referint-conf.ldif \
dna.ldif \
master-entry.ldif \

View File

@ -4,7 +4,7 @@ add: memberPrincipal
memberPrincipal: HTTP/$FQDN@$REALM
-
add: ipaAllowedTarget
ipaAllowedTarget: cn=ipa-cifs-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
ipaAllowedTarget: 'cn=ipa-cifs-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX'
dn: cn=ipa-ldap-delegation-targets,cn=s4u2proxy,cn=etc,$SUFFIX
changetype: modify

View File

@ -0,0 +1,2 @@
[global]

View File

@ -131,6 +131,7 @@ def main():
break
# Check we have a public IP that is associated with the hostname
ip = None
try:
if options.ip_address:
ip = ipautil.CheckedIPAddress(options.ip_address, match_local=True)

View File

@ -101,7 +101,10 @@ DEFAULT_CONFIG = (
('container_automember', 'cn=automember,cn=etc'),
('container_selinux', 'cn=usermap,cn=selinux'),
('container_s4u2proxy', 'cn=s4u2proxy,cn=etc'),
('container_cifsdomains', 'cn=ad,cn=etc'),
('container_trusts', 'cn=trusts'),
('container_adtrusts', 'cn=ad,cn=trusts'),
# Ports, hosts, and URIs:
# FIXME: let's renamed xmlrpc_uri to rpc_xml_uri
('xmlrpc_uri', 'http://localhost:8888/ipa/xml'),

254
ipalib/plugins/trust.py Normal file
View File

@ -0,0 +1,254 @@
# Authors:
# Alexander Bokovoy <abokovoy@redhat.com>
#
# Copyright (C) 2011 Red Hat
# see file 'COPYING' for use and warranty information
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from ipalib.plugins.baseldap import *
from ipalib import api, Str, Password, DefaultFrom, _, ngettext, Object
from ipalib.parameters import Enum
from ipalib import Command
from ipalib import errors
from ipapython import ipautil
from ipalib import util
if api.env.in_server and api.env.context in ['lite', 'server']:
try:
import ipaserver.dcerpc
_bindings_installed = True
except Exception, e:
_bindings_installed = False
__doc__ = _("""
Manage trust relationship between realms
""")
trust_output_params = (
Str('ipantflatname',
label=_('Domain NetBIOS name')),
Str('ipantsecurityidentifier',
label=_('Domain Security Identifier')),
Str('trustdirection',
label=_('Trust direction')),
Str('trusttype',
label=_('Trust type')),
Str('truststatus',
label=_('Trust status')),
)
_trust_type_dict = {1 : _('Non-Active Directory domain'),
2 : _('Active Directory domain'),
3 : _('RFC4120-compliant Kerberos realm')}
_trust_direction_dict = {1 : _('Trusting forest'),
2 : _('Trusted forest'),
3 : _('Two-way trust')}
_trust_status = {1 : _('Established and verified'),
2 : _('Waiting for confirmation by remote side')}
_trust_type_dict_unknown = _('Unknown')
def trust_type_string(level):
"""
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)
return unicode(string)
def trust_direction_string(level):
"""
Returns a string representing a direction of the trust. The original field is a bitmask taking two bits in use
LSA_TRUST_DIRECTION_INBOUND = 0x00000001,
LSA_TRUST_DIRECTION_OUTBOUND = 0x00000002
"""
string = _trust_direction_dict.get(int(level), _trust_type_dict_unknown)
return unicode(string)
def trust_status_string(level):
string = _trust_direction_dict.get(int(level), _trust_type_dict_unknown)
return unicode(string)
class trust(LDAPObject):
"""
Trust object.
"""
trust_types = ('ad', 'ipa')
container_dn = api.env.container_trusts
object_name = _('trust')
object_name_plural = _('trusts')
object_class = ['ipaNTTrustedDomain']
default_attributes = ['cn', 'ipantflatname', 'ipantsecurityidentifier',
'ipanttrusttype', 'ipanttrustattributes', 'ipanttrustdirection', 'ipanttrustpartner',
'ipantauthtrustoutgoing', 'ipanttrustauthincoming', 'ipanttrustforesttrustinfo',
'ipanttrustposixoffset', 'ipantsupportedencryptiontypes' ]
label = _('Trusts')
label_singular = _('Trust')
takes_params = (
Str('cn',
cli_name='realm',
label=_('Realm name'),
primary_key=True,
),
)
def make_trust_dn(env, trust_type, dn):
if trust_type in trust.trust_types:
container_dn = DN(('cn', trust_type), env.container_trusts, env.basedn)
return unicode(DN(DN(dn)[0], container_dn))
return dn
class trust_add_ad(LDAPCreate):
__doc__ = _('Add new trust to use against Active Directory domain.')
takes_options = (
Str('realm_admin?',
cli_name='admin',
label=_("Active Directory domain administrator"),
),
Password('realm_passwd?',
cli_name='password',
label=_("Active directory domain adminstrator's password"),
confirm=False,
),
Str('realm_server?',
cli_name='server',
label=_('Domain controller for the Active Directory domain (optional)'),
),
Password('trust_secret?',
cli_name='trust_secret',
label=_('Shared secret for the trust'),
confirm=False,
),
)
msg_summary = _('Added Active Directory trust for realm "%(value)s"')
def execute(self, *keys, **options):
# Join domain using full credentials and with random trustdom
# secret (will be generated by the join method)
trustinstance = None
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'''))
if 'realm_server' not in options:
realm_server = None
else:
realm_server = options['realm_server']
trustinstance = ipaserver.dcerpc.TrustDomainJoins(self.api)
# 1. Full access to the remote domain. Use admin credentials and
# generate random trustdom password to do work on both sides
if 'realm_admin' in options:
realm_admin = options['realm_admin']
if 'realm_passwd' not in options:
raise errors.ValidationError(name=_('AD Trust setup'), reason=_('Realm administrator password should be specified'))
realm_passwd = options['realm_passwd']
result = trustinstance.join_ad_full_credentials(keys[-1], realm_server, realm_admin, realm_passwd)
if result is None:
raise errors.ValidationError(name=_('AD Trust setup'), reason=_('Unable to verify write permissions to the AD'))
return dict(result=dict(), value=trustinstance.remote_domain.info['dns_domain'])
# 2. We don't have access to the remote domain and trustdom password
# is provided. Do the work on our side and inform what to do on remote
# side.
if 'trust_secret' in options:
result = trustinstance.join_ad_ipa_half(keys[-1], realm_server, options['trust_secret'])
return dict(result=dict(), value=trustinstance.remote_domain.info['dns_domain'])
class trust_del(LDAPDelete):
__doc__ = _('Delete a trust.')
msg_summary = _('Deleted trust "%(value)s"')
def pre_callback(self, ldap, dn, *keys, **options):
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.')
msg_summary = _('Modified trust "%(value)s"')
def pre_callback(self, ldap, dn, *keys, **options):
result = None
try:
result = self.api.Command.trust_show(keys[-1])
except errors.NotFound, e:
self.obj.handle_not_found(*keys)
# TODO: we found the trust object, now modify it
return result['result']['dn']
class trust_find(LDAPSearch):
__doc__ = _('Search for trusts.')
msg_summary = ngettext(
'%(count)d trust matched', '%(count)d trusts matched', 0
)
# 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):
return (filters, base_dn, ldap.SCOPE_SUBTREE)
class trust_show(LDAPRetrieve):
__doc__ = _('Display information about a trust.')
has_output_params = LDAPRetrieve.has_output_params + trust_output_params
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:
result['result']['trusttype'] = [trust_type_string(result['result']['ipanttrusttype'][0])]
result['result']['trustdirection'] = [trust_direction_string(result['result']['ipanttrustdirection'][0])]
break
if error or not result:
self.obj.handle_not_found(*keys)
return result
def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
if 'trust_show_type' in options:
return make_trust_dn(self.env, options['trust_show_type'], dn)
return dn
api.register(trust)
api.register(trust_add_ad)
api.register(trust_mod)
api.register(trust_del)
api.register(trust_find)
api.register(trust_show)

324
ipaserver/dcerpc.py Normal file
View File

@ -0,0 +1,324 @@
# Authors:
# Alexander Bokovoy <abokovoy@redhat.com>
#
# Copyright (C) 2011 Red Hat
# see file 'COPYING' for use and warranty information
#
# Portions (C) Andrew Tridgell, Andrew Bartlett
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
# Make sure we only run this module at the server where samba4-python
# package is installed to avoid issues with unavailable modules
from ipalib.plugins.baseldap import *
from ipalib import api, Str, Password, DefaultFrom, _, ngettext, Object
from ipalib.parameters import Enum
from ipalib import Command
from ipalib import errors
from ipapython import ipautil
from ipalib import util
import os, string, struct, copy
import uuid
from samba import param
from samba import credentials
from samba.dcerpc import security, lsa, drsblobs, nbt
from samba.ndr import ndr_pack
from samba import net
import samba
import random
import ldap as _ldap
from Crypto.Cipher import ARC4
__doc__ = _("""
Classes to manage trust joins using DCE-RPC calls
The code in this module relies heavily on samba4-python package
and Samba4 python bindings.
""")
class ExtendedDNControl(_ldap.controls.RequestControl):
def __init__(self):
self.controlType = "1.2.840.113556.1.4.529"
self.criticality = False
self.integerValue = 1
def encodeControlValue(self):
return '0\x03\x02\x01\x01'
class TrustDomainInstance(object):
def __init__(self, hostname, creds=None):
self.parm = param.LoadParm()
self.parm.load(os.path.join(ipautil.SHARE_DIR,"smb.conf.empty"))
if len(hostname) > 0:
self.parm.set('netbios name', hostname)
self.creds = creds
self.hostname = hostname
self.info = {}
self._pipe = None
self._policy_handle = None
self.read_only = False
def __gen_lsa_connection(self, binding):
if self.creds is None:
raise errors.RequirementError(name='CIFS credentials object')
try:
result = lsa.lsarpc(binding, self.parm, self.creds)
return result
except:
return None
def __init_lsa_pipe(self, remote_host):
"""
Try to initialize connection to the LSA pipe at remote host.
This method tries consequently all possible transport options
and selects one that works. See __gen_lsa_bindings() for details.
The actual result may depend on details of existing credentials.
For example, using signing causes NO_SESSION_KEY with Win2K8 and
using kerberos against Samba with signing does not work.
"""
# short-cut: if LSA pipe is initialized, skip completely
if self._pipe:
return
bindings = self.__gen_lsa_bindings(remote_host)
for binding in bindings:
self._pipe = self.__gen_lsa_connection(binding)
if self._pipe:
break
if self._pipe is None:
raise errors.RequirementError(name='Working LSA pipe')
def __gen_lsa_bindings(self, remote_host):
"""
There are multiple transports to issue LSA calls. However, depending on a
system in use they may be blocked by local operating system policies.
Generate all we can use. __init_lsa_pipe() will try them one by one until
there is one working.
We try NCACN_NP before NCACN_IP_TCP and signed sessions before unsigned.
"""
transports = (u'ncacn_np', u'ncacn_ip_tcp')
options = ( u',', u'')
binding_template=lambda x,y,z: u'%s:%s[%s]' % (x, y, z)
return [binding_template(t, remote_host, o) for t in transports for o in options]
def retrieve_anonymously(self, remote_host, discover_srv=False):
"""
When retrieving DC information anonymously, we can't get SID of the domain
"""
netrc = net.Net(creds=self.creds, lp=self.parm)
if discover_srv:
result = netrc.finddc(domain=remote_host, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
else:
result = netrc.finddc(address=remote_host, flags=nbt.NBT_SERVER_LDAP | nbt.NBT_SERVER_DS)
if not result:
return False
self.info['name'] = unicode(result.domain_name)
self.info['dns_domain'] = unicode(result.dns_domain)
self.info['dns_forest'] = unicode(result.forest)
self.info['guid'] = unicode(result.domain_uuid)
# Netlogon response doesn't contain SID of the domain.
# We need to do rootDSE search with LDAP_SERVER_EXTENDED_DN_OID control to reveal the SID
ldap_uri = 'ldap://%s' % (result.pdc_name)
conn = _ldap.initialize(ldap_uri)
conn.set_option(_ldap.OPT_SERVER_CONTROLS, [ExtendedDNControl()])
result = None
try:
(objtype, res) = conn.search_s('', _ldap.SCOPE_BASE)[0]
result = res['defaultNamingContext'][0]
self.info['dns_hostname'] = res['dnsHostName'][0]
except _ldap.LDAPError, e:
print "LDAP error when connecting to %s: %s" % (unicode(result.pdc_name), str(e))
if result:
self.info['sid'] = self.parse_naming_context(result)
return True
def parse_naming_context(self, context):
naming_ref = re.compile('.*<SID=(S-.*)>.*')
return naming_ref.match(context).group(1)
def retrieve(self, remote_host):
self.__init_lsa_pipe(remote_host)
objectAttribute = lsa.ObjectAttribute()
objectAttribute.sec_qos = lsa.QosInfo()
self._policy_handle = self._pipe.OpenPolicy2(u"", objectAttribute, security.SEC_FLAG_MAXIMUM_ALLOWED)
result = self._pipe.QueryInfoPolicy2(self._policy_handle, lsa.LSA_POLICY_INFO_DNS)
self.info['name'] = unicode(result.name.string)
self.info['dns_domain'] = unicode(result.dns_domain.string)
self.info['dns_forest'] = unicode(result.dns_forest.string)
self.info['guid'] = unicode(result.domain_guid)
self.info['sid'] = unicode(result.sid)
def generate_auth(self, trustdom_secret):
def arcfour_encrypt(key, data):
c = ARC4.new(key)
return c.encrypt(data)
def string_to_array(what):
blob = [0] * len(what)
for i in range(len(what)):
blob[i] = ord(what[i])
return blob
password_blob = string_to_array(trustdom_secret.encode('utf-16-le'))
clear_value = drsblobs.AuthInfoClear()
clear_value.size = len(password_blob)
clear_value.password = password_blob
clear_authentication_information = drsblobs.AuthenticationInformation()
clear_authentication_information.LastUpdateTime = samba.unix2nttime(int(time.time()))
clear_authentication_information.AuthType = lsa.TRUST_AUTH_TYPE_CLEAR
clear_authentication_information.AuthInfo = clear_value
authentication_information_array = drsblobs.AuthenticationInformationArray()
authentication_information_array.count = 1
authentication_information_array.array = [clear_authentication_information]
outgoing = drsblobs.trustAuthInOutBlob()
outgoing.count = 1
outgoing.current = authentication_information_array
confounder = [3]*512
for i in range(512):
confounder[i] = random.randint(0, 255)
trustpass = drsblobs.trustDomainPasswords()
trustpass.confounder = confounder
trustpass.outgoing = outgoing
trustpass.incoming = outgoing
trustpass_blob = ndr_pack(trustpass)
encrypted_trustpass = arcfour_encrypt(self._pipe.session_key, trustpass_blob)
auth_blob = lsa.DATA_BUF2()
auth_blob.size = len(encrypted_trustpass)
auth_blob.data = string_to_array(encrypted_trustpass)
auth_info = lsa.TrustDomainInfoAuthInfoInternal()
auth_info.auth_blob = auth_blob
self.auth_info = auth_info
def establish_trust(self, another_domain, trustdom_secret):
"""
Establishes trust between our and another domain
Input: another_domain -- instance of TrustDomainInstance, initialized with #retrieve call
trustdom_secret -- shared secred used for the trust
"""
self.generate_auth(trustdom_secret)
info = lsa.TrustDomainInfoInfoEx()
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_type = lsa.LSA_TRUST_TYPE_UPLEVEL
info.trust_attributes = lsa.LSA_TRUST_ATTRIBUTE_FOREST_TRANSITIVE | lsa.LSA_TRUST_ATTRIBUTE_USES_RC4_ENCRYPTION
try:
dname = lsa.String()
dname.string = another_domain.info['dns_domain']
res = self._pipe.QueryTrustedDomainInfoByName(self._policy_handle, dname, lsa.LSA_TRUSTED_DOMAIN_INFO_FULL_INFO)
self._pipe.DeleteTrustedDomain(self._policy_handle, res.info_ex.sid)
except:
pass
self._pipe.CreateTrustedDomainEx2(self._policy_handle, info, self.auth_info, security.SEC_STD_DELETE)
class TrustDomainJoins(object):
ATTR_FLATNAME = 'ipantflatname'
def __init__(self, api):
self.api = api
self.local_domain = None
self.remote_domain = None
self.ldap = self.api.Backend.ldap2
cn_trust_local = DN(('cn', self.api.env.domain), self.api.env.container_cifsdomains, self.api.env.basedn)
(dn, entry_attrs) = self.ldap.get_entry(unicode(cn_trust_local), [self.ATTR_FLATNAME])
self.local_flatname = entry_attrs[self.ATTR_FLATNAME][0]
self.local_dn = dn
self.__populate_local_domain()
def __populate_local_domain(self):
# Initialize local domain info using kerberos only
ld = TrustDomainInstance(self.local_flatname)
ld.creds = credentials.Credentials()
ld.creds.set_kerberos_state(credentials.MUST_USE_KERBEROS)
ld.creds.guess(ld.parm)
ld.creds.set_workstation(ld.hostname)
ld.retrieve(util.get_fqdn())
self.local_domain = ld
def __populate_remote_domain(self, realm, realm_server=None, realm_admin=None, realm_passwd=None):
def get_instance(self):
# Fetch data from foreign domain using password only
rd = TrustDomainInstance('')
rd.parm.set('workgroup', self.local_domain.info['name'])
rd.creds = credentials.Credentials()
rd.creds.set_kerberos_state(credentials.DONT_USE_KERBEROS)
rd.creds.guess(rd.parm)
return rd
rd = get_instance(self)
rd.creds.set_anonymous()
rd.creds.set_workstation(self.local_domain.hostname)
if realm_server is None:
rd.retrieve_anonymously(realm, discover_srv=True)
else:
rd.retrieve_anonymously(realm_server, discover_srv=False)
rd.read_only = True
if realm_admin and realm_passwd:
if 'name' in rd.info:
auth_string = u"%s\%s%%%s" % (rd.info['name'], realm_admin, realm_passwd)
td = get_instance(self)
td.creds.parse_string(auth_string)
td.creds.set_workstation(self.local_domain.hostname)
if realm_server is None:
# we must have rd.info['dns_hostname'] then, part of anonymous discovery
td.retrieve(rd.info['dns_hostname'])
else:
td.retrieve(realm_server)
td.read_only = False
self.remote_domain = td
return
# Otherwise, use anonymously obtained data
self.remote_domain = rd
def join_ad_full_credentials(self, realm, realm_server, realm_admin, realm_passwd):
self.__populate_remote_domain(realm, realm_server, realm_admin, realm_passwd)
if not self.remote_domain.read_only:
trustdom_pass = samba.generate_random_password(128, 128)
self.remote_domain.establish_trust(self.local_domain, trustdom_pass)
self.local_domain.establish_trust(self.remote_domain, trustdom_pass)
return dict(local=self.local_domain, remote=self.remote_domain)
return None
def join_ad_ipa_half(self, realm, realm_server, trustdom_passwd):
self.__populate_remote_domain(realm, realm_server, realm_passwd=None)
self.local_domain.establish_trust(self.remote_domain, trustdom_passwd)
return dict(local=self.local_domain, remote=self.remote_domain)

View File

@ -114,7 +114,7 @@ class ADTRUSTInstance(service.Service):
print "The user for Samba is %s" % self.smb_dn
try:
self.admin_conn.getEntry(self.smb_dn, ldap.SCOPE_BASE)
print "Samba user entry exists, resetting password"
root_logger.info("Samba user entry exists, resetting password")
self.admin_conn.modify_s(self.smb_dn, \
[(ldap.MOD_REPLACE, "userPassword", self.smb_dn_pwd)])
@ -215,7 +215,7 @@ class ADTRUSTInstance(service.Service):
try:
self.admin_conn.getEntry(self.smb_dom_dn, ldap.SCOPE_BASE)
print "Samba domain object already exists"
root_logger.info("Samba domain object already exists")
return
except errors.NotFound:
pass
@ -283,7 +283,12 @@ class ADTRUSTInstance(service.Service):
def __setup_principal(self):
cifs_principal = "cifs/" + self.fqdn + "@" + self.realm_name
api.Command.service_add(unicode(cifs_principal))
try:
api.Command.service_add(unicode(cifs_principal))
except errors.DuplicateEntry, e:
# CIFS principal already exists, it is not the first time adtrustinstance is managed
# That's fine, we we'll re-extract the key again.
pass
samba_keytab = "/etc/samba/samba.keytab"
if os.path.exists(samba_keytab):
@ -291,7 +296,6 @@ class ADTRUSTInstance(service.Service):
ipautil.run(["ipa-rmkeytab", "--principal", cifs_principal,
"-k", samba_keytab])
except ipautil.CalledProcessError, e:
root_logger.critical("Result of removing old key: %d" % e.returncode)
if e.returncode != 5:
root_logger.critical("Failed to remove old key for %s" % cifs_principal)
@ -374,7 +378,7 @@ class ADTRUSTInstance(service.Service):
self.ldap_enable('ADTRUST', self.fqdn, self.dm_password, \
self.suffix)
except (ldap.ALREADY_EXISTS, errors.DuplicateEntry), e:
root_logger.critical("ADTRUST Service startup entry already exists.")
root_logger.info("ADTRUST Service startup entry already exists.")
pass
def __setup_sub_dict(self):