cannatag / ldap3

a strictly RFC 4510 conforming LDAP V3 pure Python client. The same codebase works with Python 2. Python 3, PyPy and PyPy3
Other
873 stars 269 forks source link

Result mismatch while fetching from powershell and ldap3 lib #987

Open abhimani-splunk opened 3 years ago

abhimani-splunk commented 3 years ago

Hi Team,

dapsearch command not returning all the groups a user is member of, running the following search on splunk returns 17 results:

ldapsearch domain="DOMAIN" search="(&(objectclass=user)(!(objectClass=computer))(sAMAccountName=TESTUSER))" attrs=memberOf using ldap3 lib in python

This search running on Powershell returns 24 results with the same user:

(get-aduser TESTUSER -server DOMAIN -properties memberof | select -expandproperty memberof).count

Can anyone please guide here? Thanks

zorn96 commented 3 years ago

Hi @abhimani-splunk ! I think there might be some confusion here

the ldapsearch tool on unix systems, so unless you wrote your own ldapsearch from scratch in Python, it is probably not using this library.

if you do have a custom written tool, please share the source code for it. Thanks!

abhimani-splunk commented 3 years ago

Hi @zorn96

I would like to share my python code as below

def generate(self): """ :return:None`.

    """
    configuration = app.Configuration(self)

    try:
        with ldap3.Connection(
                configuration.server,
                read_only=True,
                raise_exceptions=True,
                user=configuration.credentials.username,
                password=configuration.credentials.password) as connection:

            attribute_names = app.get_normalized_attribute_names(self.attrs, connection, configuration)

            entry_generator = connection.extend.standard.paged_search(
                search_base=self.basedn, search_filter=self.search, search_scope=self.scope, attributes=self.attrs,
                paged_size=configuration.paged_size)

            encoder = JSONEncoder(ensure_ascii=False, separators=(',', ':'))
            time_stamp = time()
            serial_number = 0

            for entry in entry_generator:
                attributes = app.get_attributes(self, entry)
                if attributes:
                    dn = entry['dn']
                    yield LdapSearchCommand._record(
                        serial_number, time_stamp, connection.server.host, dn, attributes, attribute_names, encoder)
                    serial_number += 1
                    GeneratingCommand.flush
                if self.limit and serial_number == self.limit:
                    break
                pass

            pass

    except ldap3.core.exceptions.LDAPException as error:
        self.error_exit(error, app.get_ldap_error_message(error, configuration))

    return

@staticmethod
def _record(serial_number, time_stamp, host, dn, attributes, attribute_names, encoder):

    # Base-64 encode binary values (they're stored as str values--byte strings--not unicode values)

    for name, value in iteritems(attributes):
        if isinstance(value, binary_type):
            attributes[name] = b64encode(value).decode('utf-8')
        elif isinstance(value, datetime.datetime):
            attributes[name] = str(value)
        elif isinstance(value, list):
            for i in range(len(value)):
                if isinstance(value[i], binary_type):
                    value[i] = b64encode(value[i]).decode('utf-8')
                elif isinstance(value[i], datetime.datetime):
                    value[i] = str(value[i])

    raw = encoder.encode(attributes)

    # Formulate record

    if serial_number > 0:
        attributes['_serial'] = serial_number
        attributes['_time'] = time_stamp
        attributes['_raw'] = raw
        attributes['host'] = host
        attributes['dn'] = dn
        return attributes

    record = OrderedDict(chain(
        (('_serial', serial_number), ('_time', time_stamp), ('_raw', raw), ('host', host), ('dn', dn)),
        map(lambda name: (name, attributes.get(name, '')), attribute_names)))

    return record
    `
abhimani-splunk commented 3 years ago

Hello Team, Can any one please guide me? You help will be really appreciate. Thanks

zorn96 commented 3 years ago

@abhimani-splunk from your previous comment it's still very unclear what you're doing, but it seems like this is largely an implementation detail

are you using the same credentials for the python script as the logged in user in AD? and were these run at the same time? memberOf is a constructed attribute - meaning it doesn't actually exist. it's created every time it's queried and its results can vary based on domain configurations, user permissions, etc.

without actually knowing your AD setup, it's not really feasible to debug this via comments. but AD has a lot of quirks where it doesn't cleanly conform to the LDAP protocol or RFCs, and various aspects of its permissioning and group tracking are part of that