Beispiel #1
0
    async def get_all_schemaentry(self):
        """
		Fetches all Schema entries under CN=Schema,CN=Configuration,...

		:return: Async generator which yields (`MSADSchemaEntry`, None) tuple on success or (None, `Exception`) on error
		:rtype: Iterator[(:class:`MSADSchemaEntry`, :class:`Exception`)]
		"""
        res = await self.get_tree_plot('CN=Schema,CN=Configuration,' +
                                       self._tree,
                                       level=1)
        for x in res:
            for dn in res[x]:
                async for entry, err in self._con.pagedsearch(
                        dn,
                        r'(distinguishedName=%s)' % escape_filter_chars(dn),
                        attributes=[x.encode() for x in MSADSCHEMAENTRY_ATTRS],
                        size_limit=self.ldap_query_page_size,
                        search_scope=BASE,
                        controls=None,
                ):
                    if err is not None:
                        yield None, err
                        return

                    yield MSADSchemaEntry.from_ldap(entry), None
                    break
                else:
                    yield None, None

        logger.debug('Finished polling for entries!')
Beispiel #2
0
    async def get_all_service_users(self, include_machine=False):
        """
		Fetches all service user objects from the AD, and returns MSADUser object.
		Service user refers to an user with SPN (servicePrincipalName) attribute set

		:param include_machine: Specifies wether machine accounts should be included in the query
		:type include_machine: bool
		
		:return: Async generator which yields (`MSADUser`, None) tuple on success or (None, `Exception`) on error
		:rtype: Iterator[(:class:`MSADUser`, :class:`Exception`)]

		"""
        logger.debug(
            'Polling AD for all user objects, machine accounts included: %s' %
            include_machine)
        if include_machine == True:
            ldap_filter = r'(servicePrincipalName=*)'
        else:
            ldap_filter = r'(&(servicePrincipalName=*)(!(sAMAccountName=*$)))'

        async for entry, err in self.pagedsearch(ldap_filter, MSADUser_ATTRS):
            if err is not None:
                yield None, err
                return
            yield MSADUser.from_ldap(entry, self._ldapinfo), None
        logger.debug('Finished polling for entries!')
Beispiel #3
0
	async def handle_in_q(self):
		try:
			while True:
				
				preread = 6
				lb = await asyncio.wait_for(self.reader.readexactly(preread), self.timeout)
				if lb is None:
					logger.debug('Server timed out!')
					return
				if lb == b'':
					logger.debug('Server finished!')
					return

				if self.is_plain_msg is True:
					remaining_length = calcualte_length(lb) - preread
				else:
					remaining_length = int.from_bytes(lb[:4], byteorder = 'big', signed = False)
					remaining_length = (remaining_length + 4) - preread
				#print('Reading %s' % remaining_length)

				remaining_data = await asyncio.wait_for(self.reader.readexactly(remaining_length), self.timeout)
				
				await self.in_queue.put((lb+remaining_data, None))
				
		
		#except asyncio.CancelledError:
		#	return
		except Exception as e:
			#logger.exception('handle_in_q')
			await self.in_queue.put((None, e))

		finally:
			self.handle_out_task.cancel()
Beispiel #4
0
    async def get_tree_plot(self, dn, level=2):
        """
		Returns a dictionary representing a tree starting from 'dn' containing all subtrees.
		Parameters:
			dn (str): Distinguished name of the root of the tree
			level (int): Recursion level
		Returns:
			dict
		"""
        logger.debug('Tree, dn: %s level: %s' % (dn, level))
        tree = {}
        async for entry, err in self._con.pagedsearch(
                dn.encode(),
                query_syntax_converter('(distinguishedName=*)'),
                attributes=[b'distinguishedName'],
                paged_size=self.ldap_query_page_size,
                search_scope=LEVEL,
                controls=None,
        ):
            if err is not None:
                raise err

            if level == 0:
                return {}
            #print(entry)
            #print(entry['attributes']['distinguishedName'])
            if 'distinguishedName' not in entry['attributes'] or entry[
                    'attributes']['distinguishedName'] is None or entry[
                        'attributes']['distinguishedName'] == []:
                continue
            subtree = await self.get_tree_plot(
                entry['attributes']['distinguishedName'], level=level - 1)
            tree[entry['attributes']['distinguishedName']] = subtree
        return {dn: tree}
Beispiel #5
0
    async def handle_in_q(self):
        try:
            data = b''
            while True:
                while True:
                    msg_data = self.get_one_message(data)
                    if msg_data is None:
                        break

                    await self.in_queue.put((msg_data, None))
                    data = data[len(msg_data):]

                temp, err = await self.proxy_in_queue.get()
                #print(temp)
                if err is not None:
                    raise err

                if temp == b'' or temp is None:
                    logger.debug('Server finished!')
                    return

                data += temp
                continue

        except asyncio.CancelledError:
            return
        except Exception as e:
            logger.exception('handle_in_q')
            await self.in_queue.put((None, e))

        finally:
            self.proxy_task.cancel()
Beispiel #6
0
    async def get_all_spn_entries(self):
        logger.debug('Polling AD for all SPN entries')
        ldap_filter = r'(&(sAMAccountType=805306369))'
        attributes = ['objectSid', 'sAMAccountName', 'servicePrincipalName']

        async for entry in self.pagedsearch(ldap_filter, attributes):
            yield entry
Beispiel #7
0
    async def connect(self):
        """
		Connects to the remote server. Establishes the session, but doesn't perform binding.
		This function MUST be called first before the `bind` operation.

		:return: A tuple of (True, None) on success or (False, Exception) on error. 
		:rtype: (:class:`bool`, :class:`Exception`)
		"""
        try:
            logger.debug('Connecting!')
            self.network = await MSLDAPNetworkSelector.select(self.target)
            res, err = await self.network.run()
            if res is False:
                return False, err

            # now processing channel binding options
            if self.target.proto == LDAPProtocol.SSL:
                certdata = self.network.get_peer_certificate()
                #cert = Certificate.load(certdata).native
                #print(cert)
                cb_struct = ChannelBindingsStruct()
                cb_struct.application_data = b'tls-server-end-point:' + sha256(
                    certdata).digest()

                self.cb_data = cb_struct.to_bytes()

            self.handle_incoming_task = asyncio.create_task(
                self.__handle_incoming())
            logger.debug('Connection succsessful!')
            return True, None
        except Exception as e:
            return False, e
Beispiel #8
0
    async def connect(self):
        try:
            logger.debug('Connecting!')
            self.network = await MSLDAPNetworkSelector.select(self.target)
            res, err = await self.network.run()
            if res is False:
                return False, err

            # now processing channel binding options
            if self.target.proto == LDAPProtocol.SSL:
                certdata = self.network.get_peer_certificate()
                #cert = Certificate.load(certdata).native
                #print(cert)
                cb_struct = ChannelBindingsStruct()
                cb_struct.application_data = b'tls-server-end-point:' + sha256(
                    certdata).digest()

                self.cb_data = cb_struct.to_bytes()

            self.handle_incoming_task = asyncio.create_task(
                self.__handle_incoming())
            logger.debug('Connection succsessful!')
            return True, None
        except Exception as e:
            return False, e
Beispiel #9
0
    def get_tree_plot(self, dn, level=2):
        logger.debug('Tree, dn: %s level: %s' % (dn, level))
        tree = {}
        entries = self._con.extend.standard.paged_search(
            dn,
            '(distinguishedName=*)',
            attributes='distinguishedName',
            paged_size=self.ldap_query_page_size,
            search_scope=LEVEL,
            controls=None)
        for entry in entries:

            if 'raw_attributes' in entry and 'attributes' in entry:
                # TODO: return ldapuser object
                if level == 0:
                    return {}

                #print(entry['attributes']['distinguishedName'])
                if entry['attributes']['distinguishedName'] is None or entry[
                        'attributes']['distinguishedName'] == []:
                    continue
                subtree = self.get_tree_plot(
                    entry['attributes']['distinguishedName'], level=level - 1)
                tree[entry['attributes']['distinguishedName']] = subtree
        return {dn: tree}
Beispiel #10
0
    async def get_all_user_raw(self):
        """
		Fetches all user objects from the AD, and returns MSADUser object
		"""
        logger.debug('Polling AD for all user objects')
        ldap_filter = r'(sAMAccountType=805306368)'

        return self.pagedsearch(ldap_filter, MSADUser_ATTRS)
Beispiel #11
0
    async def pagedsearch(self, ldap_filter, attributes, controls=None):
        """
		Performs a paged search on the AD, using the filter and attributes as a normal query does.
		Needs to connect to the server first!

		Parameters:
			ldap_filter (str): LDAP query filter
			attributes (list): Attributes list to recieve in the result
			controls (obj): Additional control dict
		
		Returns:
			generator
		"""
        logger.debug('Paged search, filter: %s attributes: %s' %
                     (ldap_filter, ','.join(attributes)))
        if self._con.status != MSLDAPClientStatus.RUNNING:
            if self._con.status == MSLDAPClientStatus.ERROR:
                print('There was an error in the connection!')
                return
            elif self._con.status == MSLDAPClientStatus.ERROR:
                print('Theconnection is in stopped state!')
                return

        if self._tree is None:
            raise Exception('BIND first!')
        t = []
        for x in attributes:
            t.append(x.encode())
        attributes = t
        ldap_filter = query_syntax_converter(ldap_filter)

        t = []
        if controls is not None:
            for control in controls:
                t.append(
                    Control({
                        'controlType': control[0].encode(),
                        'criticality': control[1],
                        'controlValue': control[2]
                    }))

        controls = t

        async for entry, err in self._con.pagedsearch(
                self._tree.encode(),
                ldap_filter,
                attributes=attributes,
                paged_size=self.ldap_query_page_size,
                controls=controls):

            if err is not None:
                raise err
            if entry['objectName'] == '' and entry['attributes'] == '':
                #searchresref...
                continue
            #print('et %s ' % entry)
            yield entry
Beispiel #12
0
    async def get_all_user_objects(self):
        """
		Fetches all user objects from the AD, and returns MSADUser object
		"""
        logger.debug('Polling AD for all user objects')
        ldap_filter = r'(sAMAccountType=805306368)'
        async for entry in self.pagedsearch(ldap_filter, MSADUser_ATTRS):
            yield MSADUser.from_ldap(entry, self._ldapinfo)
        logger.debug('Finished polling for entries!')
Beispiel #13
0
    async def get_user(self, sAMAccountName):
        """
		Fetches one user object from the AD, based on the sAMAccountName attribute (read: username) 
		"""
        logger.debug('Polling AD for user %s' % sAMAccountName)
        ldap_filter = r'(&(objectClass=user)(sAMAccountName=%s))' % sAMAccountName
        async for entry in self.pagedsearch(ldap_filter, MSADUser_ATTRS):
            # TODO: return ldapuser object
            yield MSADUser.from_ldap(entry, self._ldapinfo)
        logger.debug('Finished polling for entries!')
Beispiel #14
0
    async def get_ad_info(self):
        """
		Polls for basic AD information (needed for determine password usage characteristics!)
		"""
        logger.debug('Polling AD for basic info')
        ldap_filter = r'(distinguishedName=%s)' % self._tree
        async for entry in self.pagedsearch(ldap_filter, MSADInfo_ATTRS):
            self._ldapinfo = MSADInfo.from_ldap(entry)
            return self._ldapinfo

        logger.debug('Poll finished!')
Beispiel #15
0
    def get_all_user_objects(self):
        """
        Fetches all user objects from the AD, and returns MSADUser object
        """
        logger.debug('Polling AD for all user objects')
        ldap_filter = r'(objectClass=user)'

        attributes = MSADUser.ATTRS
        for entry in self.pagedsearch(ldap_filter, attributes):
            yield MSADUser.from_ldap(entry, self._ldapinfo)
        logger.debug('Finished polling for entries!')
Beispiel #16
0
    def get_all_machine_objects(self):
        """
        Fetches all machine objects from the AD, and returns MSADMachine object
        """
        logger.debug('Polling AD for all user objects')
        ldap_filter = r'(&(sAMAccountType=805306369))'

        attributes = MSADMachine.ATTRS
        for entry in self.pagedsearch(ldap_filter, attributes):
            yield MSADMachine.from_ldap(entry, self._ldapinfo)
        logger.debug('Finished polling for entries!')
Beispiel #17
0
    def __bind_success(self):
        """
		Internal function invoked after bind finished. 
		Instructs the network layer that upcoming messages might be wrapped
		"""
        logger.debug('BIND Success!')
        self.bind_ok = True
        if self.creds.auth_method in MSLDAP_GSS_METHODS or self.creds.auth_method == LDAPAuthProtocol.SICILY:
            self.__sign_messages = self.auth.signing_needed()
            self.__encrypt_messages = self.auth.encryption_needed()
            if self.__encrypt_messages or self.__sign_messages:
                self.network.is_plain_msg = False
Beispiel #18
0
    async def disconnect(self):
        """
		Tears down the connection.

		:return: Nothing
		:rtype: None
		"""

        logger.debug('Disconnecting!')
        self.bind_ok = False
        self.handle_incoming_task.cancel()
        await self.network.terminate()
Beispiel #19
0
def convert_attributes(x):
	t = {}
	for e in x:
		#print(e)
		k = e['type'].decode()
		#print('k: %s' % k)
		if k in LDAP_ATTRIBUTE_TYPES:
			t[k] = LDAP_ATTRIBUTE_TYPES[k](e['attributes'])
		else:
			logger.debug('Unknown type! %s data: %s' % (k, e['attributes']))
			t[k] = e['attributes']
	return t
Beispiel #20
0
    async def login(self):
        """Performs connection and login"""
        try:
            logger.debug(self.conn_url.get_credential())
            logger.debug(self.conn_url.get_target())

            self.connection = self.conn_url.get_client()
            _, err = await self.connection.connect()
            if err is not None:
                raise err

            return True, None
        except Exception as e:
            return False, e
Beispiel #21
0
	async def recv_message(self, message_id):
		if message_id not in self.message_table_notify:
			logger.debug('Requested message id %s which is not in the message notify table!' % message_id)
			return None
		#print('Waiting for %s' % message_id)
		await self.message_table_notify[message_id].wait()
		#print(self.message_table)
		messages = self.message_table[message_id]

		#print('%s arrived!' % message_id)

		self.message_table[message_id] = []
		self.message_table_notify[message_id].clear()

		return messages
Beispiel #22
0
    async def get_ad_info(self):
        """
		Polls for basic AD information (needed for determine password usage characteristics!)
		
		:return: A tuple with the domain information as `MSADInfo` and an `Exception` is there was any
		:rtype: (:class:`MSADInfo`, :class:`Exception`)
		"""
        logger.debug('Polling AD for basic info')
        ldap_filter = r'(distinguishedName=%s)' % self._tree
        async for entry, err in self.pagedsearch(ldap_filter, MSADInfo_ATTRS):
            if err is not None:
                return None, err
            self._ldapinfo = MSADInfo.from_ldap(entry)
            return self._ldapinfo, None

        logger.debug('Poll finished!')
Beispiel #23
0
    async def get_all_users(self):
        """
		Fetches all user objects available in the LDAP tree and yields them as MSADUser object.
		
		:return: Async generator which yields (`MSADUser`, None) tuple on success or (None, `Exception`) on error
		:rtype: Iterator[(:class:`MSADUser`, :class:`Exception`)]
		
		"""
        logger.debug('Polling AD for all user objects')
        ldap_filter = r'(sAMAccountType=805306368)'
        async for entry, err in self.pagedsearch(ldap_filter, MSADUser_ATTRS):
            if err is not None:
                yield None, err
                return
            yield MSADUser.from_ldap(entry, self._ldapinfo), None
        logger.debug('Finished polling for entries!')
Beispiel #24
0
    def get_all_knoreq_user_objects(self, include_machine = False):
        """
        Fetches all user objects with useraccountcontrol DONT_REQ_PREAUTH flag set from the AD, and returns MSADUser object.
        
        """
        logger.debug('Polling AD for all user objects, machine accounts included: %s'% include_machine)
        if include_machine == True:
            ldap_filter = r'(userAccountControl:1.2.840.113556.1.4.803:=4194304)'
        else:
            ldap_filter = r'(&(userAccountControl:1.2.840.113556.1.4.803:=4194304)(!(sAMAccountName = *$)))'

        attributes = MSADUser.ATTRS
        for entry in self.pagedsearch(ldap_filter, attributes):
            # TODO: return ldapuser object
            yield MSADUser.from_ldap(entry, self._ldapinfo)
        logger.debug('Finished polling for entries!')
Beispiel #25
0
    def get_all_service_user_objects(self, include_machine = False):
        """
        Fetches all service user objects from the AD, and returns MSADUser object.
        Service user refers to an user whith SPN (servicePrincipalName) attribute set
        """
        logger.debug('Polling AD for all user objects, machine accounts included: %s'% include_machine)
        if include_machine == True:
            ldap_filter = r'(servicePrincipalName=*)'
        else:
            ldap_filter = r'(&(servicePrincipalName=*)(!(sAMAccountName = *$)))'

        attributes = MSADUser.ATTRS
        for entry in self.pagedsearch(ldap_filter, attributes):
            # TODO: return ldapuser object
            yield MSADUser.from_ldap(entry, self._ldapinfo)
        logger.debug('Finished polling for entries!')
Beispiel #26
0
    def connect(self):
        if self.login_credential.is_anonymous() == True:
            logger.debug(
                'Getting server info via Anonymous BIND on server %s' %
                self.target_server.get_host())
            self._srv = Server(self.target_server.get_host(),
                               use_ssl=self.target_server.is_ssl(),
                               get_info=ALL)
            self._con = Connection(self._srv, auto_bind=True)
        else:
            self._srv = Server(self.target_server.get_host(),
                               use_ssl=self.target_server.is_ssl(),
                               get_info=ALL)
            if self.login_credential.secret_type == MSLDAPSecretType.SSPI:
                self.monkeypatch()

            self._con = Connection(
                self._srv,
                user=self.login_credential.get_msuser(),
                password=self.login_credential.get_password(),
                authentication=self.login_credential.get_authmethod(),
                auto_bind=True)
            logger.debug('Performing BIND to server %s' %
                         self.target_server.get_host())

        if not self._con.bind():
            if 'description' in self._con.result:
                raise Exception('Failed to bind to server! Reason: %s' %
                                self._con.result['description'])
            raise Exception('Failed to bind to server! Reason: %s' %
                            self._con.result)

        if not self._tree:
            logger.debug('Search tree base not defined, selecting root tree')
            info = self.get_server_info()
            if 'rootDomainNamingContext' not in info.other:
                #really rare cases, the DC doesnt reply to DSA requests!!!
                #in this case you will need to manually instruct the connection object on which tree it should perform the searches on
                raise Exception(
                    'Could not get the rootDomainNamingContext! You will need to specify the "tree" parameter manually!'
                )

            self._tree = info.other['rootDomainNamingContext'][0]
            logger.debug('Selected tree: %s' % self._tree)

        logger.debug('Connected to server!')
Beispiel #27
0
 def pagedsearch(self, ldap_filter, attributes, controls = None):
     """
     Performs a paged search on the AD, using the filter and attributes as a normal query does.
     Needs to connect to the server first!
     ldap_filter: str : LDAP query filter
     attributes: list : Attributes list to recieve in the result
     """
     logger.debug('Paged search, filter: %s attributes: %s' % (ldap_filter, ','.join(attributes)))
     ctr = 0
     entries = self._con.extend.standard.paged_search(self._tree, ldap_filter, attributes = attributes, paged_size = self.ldap_query_page_size, controls = controls)
     for entry in entries:
         if 'raw_attributes' in entry and 'attributes' in entry:
             # TODO: return ldapuser object
             ctr += 1
             if ctr % self.ldap_query_page_size == 0:
                 logger.info('New page requested. Result count: %d' % ctr)
             yield entry
Beispiel #28
0
    async def get_all_spn_entries(self):
        """
		Fetches all service user objects from the AD, and returns MSADUser object.
		Service user refers to an user with SPN (servicePrincipalName) attribute set

		:param include_machine: Specifies wether machine accounts should be included in the query
		:type include_machine: bool
		:return: Async generator which yields tuples with a string in SPN format and an Exception if there was any
		:rtype: Iterator[(:class:`str`, :class:`Exception`)]
		
		"""

        logger.debug('Polling AD for all SPN entries')
        ldap_filter = r'(&(sAMAccountType=805306369))'
        attributes = ['objectSid', 'sAMAccountName', 'servicePrincipalName']

        async for entry, err in self.pagedsearch(ldap_filter, attributes):
            yield entry, err
Beispiel #29
0
    async def get_user(self, sAMAccountName):
        """
		Fetches one user object from the AD, based on the sAMAccountName attribute (read: username) 
		
		:param sAMAccountName: The username of the user.
		:type sAMAccountName: str
		:return: A tuple with the user as `MSADUser` and an `Exception` is there was any
		:rtype: (:class:`MSADUser`, :class:`Exception`)
		"""
        logger.debug('Polling AD for user %s' % sAMAccountName)
        ldap_filter = r'(&(objectClass=user)(sAMAccountName=%s))' % sAMAccountName
        async for entry, err in self.pagedsearch(ldap_filter, MSADUser_ATTRS):
            if err is not None:
                return None, err
            return MSADUser.from_ldap(entry, self._ldapinfo), None
        else:
            return None, None
        logger.debug('Finished polling for entries!')
Beispiel #30
0
	async def handle_out_q(self):
		try:
			while True:
				data = await self.out_queue.get()
				if data is None:
					logger.debug('Client finished!')
					return

				self.writer.write(data)
				await self.writer.drain()
		except asyncio.CancelledError:
			return
		except:
			logger.exception('handle_out_q')
		
		finally:
			self.writer.close()
			self.handle_in_task.cancel()