summaryrefslogtreecommitdiffstats
path: root/ipaserver/dcerpc.py
diff options
context:
space:
mode:
Diffstat (limited to 'ipaserver/dcerpc.py')
-rw-r--r--ipaserver/dcerpc.py230
1 files changed, 198 insertions, 32 deletions
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
index 0f98ce83c..a3b1a444b 100644
--- a/ipaserver/dcerpc.py
+++ b/ipaserver/dcerpc.py
@@ -53,6 +53,7 @@ from ipapython.ipaldap import IPAdmin
from ipalib.session import krbccache_dir, krbccache_prefix
from dns import resolver, rdatatype
from dns.exception import DNSException
+from time import sleep
__doc__ = _("""
Classes to manage trust joins using DCE-RPC calls
@@ -61,6 +62,7 @@ The code in this module relies heavily on samba4-python package
and Samba4 python bindings.
""")
+
def is_sid_valid(sid):
try:
security.dom_sid(sid)
@@ -69,6 +71,55 @@ def is_sid_valid(sid):
else:
return True
+
+def kinit_as_http(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
+ """
+
+ domain_suffix = domain.replace('.', '-')
+
+ ccache_name = "%sTD%s" % (krbccache_prefix, domain_suffix)
+ ccache_path = os.path.join(krbccache_dir, ccache_name)
+
+ realm = api.env.realm
+ hostname = api.env.host
+ principal = 'HTTP/%s@%s' % (hostname, realm)
+ keytab = '/etc/httpd/conf/ipa.keytab'
+
+ # Destroy the contents of the ccache
+ root_logger.info('Destroying the contents of the separate ccache')
+
+ (stdout, stderr, returncode) = ipautil.run(
+ ['/usr/bin/kdestroy', '-A', '-c', ccache_path],
+ env={'KRB5CCNAME': ccache_path},
+ raiseonerr=False, debug=True)
+
+ root_logger.warning('kdestroy: %s' % stdout)
+ root_logger.warning('kdestroy: %s' % stderr)
+ root_logger.warning('kdestroy: %s' % str(returncode))
+
+ # Destroy the contents of the ccache
+ root_logger.info('Running kinit from ipa.keytab to obtain HTTP service'
+ 'principal with MS-PAC attached.')
+
+ (stdout, stderr, returncode) = ipautil.run(
+ ['/usr/bin/kinit', '-kt', keytab, principal],
+ env={'KRB5CCNAME': ccache_path},
+ raiseonerr=False, debug=True)
+
+ if returncode == 0:
+ return (ccache_path, principal)
+ else:
+ return (None, None)
+
access_denied_error = errors.ACIError(info=_('CIFS server denied your credentials'))
dcerpc_error_codes = {
-1073741823:
@@ -113,6 +164,7 @@ class ExtendedDNControl(LDAPControl):
def encodeControlValue(self, value=None):
return '0\x03\x02\x01\x01'
+
class DomainValidator(object):
ATTR_FLATNAME = 'ipantflatname'
ATTR_SID = 'ipantsecurityidentifier'
@@ -184,6 +236,28 @@ class DomainValidator(object):
except errors.NotFound, e:
return []
+ def set_trusted_domains(self):
+ # At this point we have SID_NT_AUTHORITY family SID and really need to
+ # check it against prefixes of domain SIDs we trust to
+ if not self._domains:
+ self._domains = self.get_trusted_domains()
+ if len(self._domains) == 0:
+ # Our domain is configured but no trusted domains are configured
+ # This means we can't check the correctness of a trusted
+ # domain SIDs
+ raise errors.ValidationError(name='sid',
+ error=_('no trusted domain is configured'))
+
+ def get_basedn(self, domain):
+ info = self.__retrieve_trusted_domain_gc_list(domain)
+
+ if not info:
+ raise errors.ValidationError(name=_('Trust setup'),
+ error=_('Cannot retrieve trusted domain GC list'))
+
+ basedn = DN(*map(lambda p: ('dc', p), info['dns_domain'].split('.')))
+ return basedn
+
def get_domain_by_sid(self, sid, exact_match=False):
if not self.domain:
# our domain is not configured or self.is_configured() never run
@@ -200,14 +274,7 @@ class DomainValidator(object):
# At this point we have SID_NT_AUTHORITY family SID and really need to
# check it against prefixes of domain SIDs we trust to
- if not self._domains:
- self._domains = self.get_trusted_domains()
- if len(self._domains) == 0:
- # Our domain is configured but no trusted domains are configured
- # This means we can't check the correctness of a trusted
- # domain SIDs
- raise errors.ValidationError(name='sid',
- error=_('no trusted domain is configured'))
+ self.set_trusted_domains()
# We have non-zero list of trusted domains and have to go through
# them one by one and check their sids as prefixes / exact match
@@ -436,48 +503,143 @@ class DomainValidator(object):
dict(domain=info['dns_domain'],message=stderr.strip()))
return (None, None)
- def search_in_gc(self, domain, filter, attrs, scope, basedn=None):
+ def search_in_gc(self, domain, filter, attrs, scope, basedn=None,
+ use_http=False):
"""
Perform LDAP search in a trusted domain `domain' Global Catalog.
- Returns resulting entries or None
+ Returns resulting entries or None.
+
+ If use_http is set to True, the search is conducted using
+ HTTP service credentials.
"""
+
entries = None
- sid = None
+
info = self.__retrieve_trusted_domain_gc_list(domain)
+
if not info:
- raise errors.ValidationError(name=_('Trust setup'),
+ raise errors.ValidationError(
+ name=_('Trust setup'),
error=_('Cannot retrieve trusted domain GC list'))
+
for (host, port) in info['gc']:
- entries = self.__search_in_gc(info, host, port, filter, attrs, scope, basedn)
+ entries = self.__search_in_gc(info, host, port, filter, attrs,
+ scope, basedn=basedn,
+ use_http=use_http)
if entries:
break
return entries
- def __search_in_gc(self, info, host, port, filter, attrs, scope, basedn=None):
+ def __search_in_gc(self, info, host, port, filter, attrs, scope,
+ basedn=None, use_http=False):
"""
Actual search in AD LDAP server, using SASL GSSAPI authentication
- Returns LDAP result or None
+ Returns LDAP result or None.
"""
conn = IPAdmin(host=host, port=port, no_schema=True, decode_attrs=False)
- auth = self.__extract_trusted_auth(info)
- if attrs is None:
- attrs = []
- if auth:
- (ccache_name, principal) = self.__kinit_as_trusted_account(info, auth)
- if ccache_name:
- old_ccache = os.environ.get('KRB5CCNAME')
- os.environ["KRB5CCNAME"] = ccache_name
- # OPT_X_SASL_NOCANON is used to avoid hard requirement for PTR
- # records pointing back to the same host name
- conn.set_option(_ldap.OPT_X_SASL_NOCANON, _ldap.OPT_ON)
- conn.do_sasl_gssapi_bind()
- if basedn is None:
- # Use domain root base DN
- basedn = DN(*map(lambda p: ('dc', p), info['dns_domain'].split('.')))
- entries = conn.get_entries(basedn, scope, filter, attrs)
+
+ if use_http:
+ (ccache_name, principal) = kinit_as_http(info['dns_domain'])
+ else:
+ auth = self.__extract_trusted_auth(info)
+
+ if not auth:
+ return None
+
+ (ccache_name, principal) = self.__kinit_as_trusted_account(info,
+ auth)
+
+ if ccache_name:
+ old_ccache = os.environ.get('KRB5CCNAME')
+ os.environ["KRB5CCNAME"] = ccache_name
+
+ # Printing irrelevant debugging info
+ root_logger.info('Printing irrelevant (so far) debug info:')
+ root_logger.info('ccache: %s' % ccache_name)
+ root_logger.info('host: %s' % host)
+ root_logger.info('port: %s' % port)
+ root_logger.info('filter: %s' % filter)
+ root_logger.info('attrs: %s' % attrs)
+
+ # OPT_X_SASL_NOCANON is used to avoid hard requirement for PTR
+ # records pointing back to the same host name
+ conn.set_option(_ldap.OPT_X_SASL_NOCANON, _ldap.OPT_ON)
+ conn.do_sasl_gssapi_bind()
+
+ if basedn is None:
+ # Use domain root base DN
+ basedn = ipautil.realm_to_suffix(info['dns_domain'])
+
+ # Let's get some debugging going
+ root_logger.info('Printing contents of the ccache:')
+ o, e, r = ipautil.run(['klist'], debug=True)
+ root_logger.info("klist: %s" % o)
+ root_logger.info("klist error: %s" % e)
+
+ # How big are the keytabs?
+ root_logger.info('The size of /etc/httpd/conf/ipa.keytab:')
+ o, e, r = ipautil.run(['du', '-h', '/etc/httpd/conf/ipa.keytab'],
+ debug=True)
+ root_logger.info("du: %s" % o)
+ root_logger.info("du error: %s" % e)
+
+ root_logger.info('The size of /etc/dirsrv/ds.keytab:')
+ o, e, r = ipautil.run(['du', '-h', '/etc/dirsrv/ds.keytab'],
+ debug=True)
+ root_logger.info("du: %s" % o)
+ root_logger.info("du error: %s" % e)
+
+ # How big is the ccache?
+ root_logger.info('The size of ccache %s' % ccache_name)
+ o, e, r = ipautil.run(['du', '-h', ccache_name],
+ debug=True)
+ root_logger.info("du: %s" % o)
+ root_logger.info("du error: %s" % e)
+
+ # Run the ldapsearch from within the framework
+ root_logger.info('Conducting ldapsearch:')
+ o, e, r = ipautil.run(
+ ['ldapsearch',
+ '-Y', 'GSSAPI',
+ '-U', 'HTTP/{local}@{dom}'.format(local=api.env.host,
+ dom=api.env.realm),
+ '-H', 'ldap://%s' % host,
+ '-b', str(basedn),
+ filter],
+ raiseonerr=False, debug=True,
+ env={'KRB5CCNAME': ccache_name})
+
+ root_logger.info("ldapsearch: %s" % o)
+ root_logger.warning("ldapsearch error: %s" % e)
+
+ result = 'nothing'
+ result2= 'nothing'
+ try:
+ connection = _ldap.initialize('ldap://{host}'.format(host=host))
+ auth2 = _ldap.sasl.gssapi('')
+ connection.sasl_interactive_bind_s('', auth2)
+ result = connection.search_s(str(basedn), _ldap.SCOPE_SUBTREE,
+ filter)
+ result2 = connection.search_ext(str(basedn), _ldap.SCOPE_SUBTREE,
+ filter)
+ except Exception, e:
+ root_logger.error('direct ldap error: %s' % str(e))
+
+ root_logger.error('direct ldap result: %s' % result)
+ root_logger.error('direct ldap result2: %s' % result2)
+
+ try:
+ entries = conn.get_entries(basedn, scope, filter, attrs, debug=True)
+ except:
+ root_logger.error('framework ldap error: %s' % str(e))
+ entries = None
+ finally:
os.environ["KRB5CCNAME"] = old_ccache
- return entries
+
+ root_logger.error('framework ldap result: %s' % entries)
+
+ return entries
def __retrieve_trusted_domain_gc_list(self, domain):
"""
@@ -508,9 +670,13 @@ class DomainValidator(object):
except RuntimeError, e:
finddc_error = e
+ if not self._domains:
+ self._domains = self.get_trusted_domains()
+
info = dict()
info['auth'] = self._domains[domain][2]
servers = []
+
if result:
info['name'] = unicode(result.domain_name)
info['dns_domain'] = unicode(result.dns_domain)