def ldap_search_has_results(bound_client, search_dn_str, filter_object):
    """
    Determine if an ldap filter will return any results.  The exact count will not be determined, just whether
    the search will return zero or 1+ results.

    Args:
        bound_client: An ldaptor client that's already connected and bound as a search user
        search_dn_str (str): the base DN to search from
        filter_object: the filter text for the search, in LDAP filter format

    Returns:
        LdapSearchResult: the result of the test
    """
    search_dn = DistinguishedName(search_dn_str)

    try:
        search_results = yield bound_client.perform_search(search_dn,
                                                           filter_object,
                                                           sizeLimit=1)
    except Exception as e:
        result = LdapSearchResult(False,
                                  search_dn_str,
                                  filter_object,
                                  exception=e)
    else:
        if len(search_results) > 0:
            result = LdapSearchResult(True, search_dn_str, filter_object)
        else:
            result = LdapSearchResult(False, search_dn_str, filter_object)

    defer.returnValue(result)
Beispiel #2
0
def main(dns):
    l = []
    resolver = client.Resolver("/etc/resolv.conf")

    for dnString in dns:
        dn = DistinguishedName(stringValue=dnString)
        domain = dn.getDomainName()

        d = resolver.lookupService("_ldap._tcp.%s" % domain)
        l.append(d)
        d.addCallback(printAnswer, dnString)
        d.addErrback(errback)
    dl = DeferredList(l)
    dl.addBoth(lambda dummy: reactor.callLater(0, reactor.stop))
    reactor.run()
    sys.exit(exitStatus)
 def _is_under_exempt_ous(self, user_identifier, identifier_location):
     """Returns true if the user is found to be in one of the configured exempt groups
     Args:
         user_identifier: Username or DN to lookup
         identifier_location: One of utilities.LDAP_USERNAME_FROM_*. Explain where the username came from.
                              DN field of the LDAP packet, NTLM username field, etc.
     """
     if not self.exempt_ous:
         defer.returnValue(False)
     user_identifier = user_identifier.lower()
     try:
         dn = DistinguishedName(user_identifier)
     except Exception:
         # Response DNs shouldn't be invalid but a request's DN
         # field may be a NetBIOS (domain\username), a UPN (username@domain), a samAccountName (username), or a common name (username)".
         # Implementing exempt_ous for such binds requires an additional search
         # in order to get the DN.
         log.msg(
             "Bind Request did not have a full DN. Attempting to lookup full DN for {}".format(
                 user_identifier
             )
         )
         possible_username = utilities.LdapUsername(
             user_identifier, identifier_location
         )
         try:
             dn = yield self.username_to_dn(possible_username)
         except DnLookupFailed as e:
             log.msg(
                 "Failed to lookup full DN. Marking as non-exempt by default. Error: {}".format(
                     e
                 )
             )
             defer.returnValue(False)
     defer.returnValue(any([base_dn.contains(dn) for base_dn in self.exempt_ous]))
def normalize_dn(dn):
    """
    Transforms a `DistinguishedName` into a `DistinguishedName` with the casing normalized.
    """
    return DistinguishedName(
        tuple(
            RelativeDistinguishedName(r.getText().lower())
            for r in dn.listOfRDNs))
Beispiel #5
0
    def test_parseOptions_multiple(self):
        """
        It can have have multiple --service-location options and they are
        indexed using the base DN.
        """
        sut = ServiceLocationOptionsImplementation()

        sut.parseOptions(options=[
            '--service-location', 'dc=example,dc=com:127.0.0.1',
            '--service-location', 'dc=example,dc=org:172.0.0.1',
            ])

        base_com = DistinguishedName('dc=example,dc=com')
        base_org = DistinguishedName('dc=example,dc=org')
        value_com = sut.opts['service-location'][base_com]
        value_org = sut.opts['service-location'][base_org]
        self.assertEqual(('127.0.0.1', None), value_com)
        self.assertEqual(('172.0.0.1', None), value_org)
Beispiel #6
0
    def test_parseOptions_multiple(self):
        """
        It can have have multiple --service-location options and they are
        indexed using the base DN.
        """
        sut = ServiceLocationOptionsImplementation()

        sut.parseOptions(options=[
            "--service-location",
            "dc=example,dc=com:127.0.0.1",
            "--service-location",
            "dc=example,dc=org:172.0.0.1",
        ])

        base_com = DistinguishedName("dc=example,dc=com")
        base_org = DistinguishedName("dc=example,dc=org")
        value_com = sut.opts["service-location"][base_com]
        value_org = sut.opts["service-location"][base_org]
        self.assertEqual(("127.0.0.1", None), value_com)
        self.assertEqual(("172.0.0.1", None), value_org)
Beispiel #7
0
    def test_parseOptions_single(self):
        """
        It can have a single --service-location option.
        """
        sut = ServiceLocationOptionsImplementation()

        sut.parseOptions(options=[
            '--service-location', 'dc=example,dc=com:127.0.0.1:1234'])

        base = DistinguishedName('dc=example,dc=com')
        value = sut.opts['service-location'][base]
        self.assertEqual(('127.0.0.1', '1234'), value)
Beispiel #8
0
    def test_parseOptions_single(self):
        """
        It can have a single --service-location option.
        """
        sut = ServiceLocationOptionsImplementation()

        sut.parseOptions(
            options=["--service-location", "dc=example,dc=com:127.0.0.1:1234"])

        base = DistinguishedName("dc=example,dc=com")
        value = sut.opts["service-location"][base]
        self.assertEqual(("127.0.0.1", "1234"), value)
Beispiel #9
0
    def dn_to_username(self, dn_str: str, client_factory):
        """
        Return a username usable for Duo auth.

        The return value may include a domain. If necessary, that must
        be handled with a domain username normalization policy on the
        Duo integration.
        Args:
            dn_str: (str)
            client_factory (ADClientFactory)
        Returns:
            str: The username we want to send to Duo
            None: If we could not determine the username to send
        """
        try:
            dn = DistinguishedName(dn_str)
        except Exception:
            # At this point we think the DN is actually in a username format
            # but we'll need some extra searches to verify for sure.
            # This is technically not legal LDAP to do this, but Active Directory
            # supports it so we must as well.
            possible_username = utilities.LdapUsername(
                dn_str, LdapUsernameOrigin.BIND_DN)
            try:
                _ = yield self.validate_ldap_username_for_auth(
                    possible_username, client_factory.search_dn)
            except NoUserFound as e:
                log.msg(str(e))
                defer.returnValue(None)
            else:
                # If our validate call didn't throw an exception that means our "DN" was a valid username
                # and we can just return that for usage with our call to Duo
                defer.returnValue(dn_str)

        entry = LDAPEntry(self, dn)
        user_filter = yield self.user_filter_object()
        result = yield entry.search(
            filterObject=user_filter,
            attributes=(self.factory.username_attribute, ),
        )

        if len(result) != 1:
            defer.returnValue(None)

        attr_set = result[0].get(self.factory.username_attribute)

        if not attr_set:
            defer.returnValue(None)

        defer.returnValue(list(attr_set)[0].decode())
    def __init__(
        self,
        factors,
        primary_ator,
        failmode,
        exempt_primary_bind=True,
        exempt_ous=None,
        allow_searches_after_bind=True,
        allow_unlimited_binds=False,
        **kwargs
    ):
        """
        Duo LDAP server with secondary auth factor pre-selected by admin.

        factors: comma-separated list of factor names like "auto,push".

        exempt_primary_bind: If True, Duo auth will not be performed
        for the first bind request in a session. Otherwise, it will be
        performed for every successful bind.

        exempt_ous: List of base DNs (DistinguishedName objects or
        strings). Duo auth will not be performed for any bind request
        with a DN under one of the listed trees.

        resolving_bind_request: True if a valid BindRequest has been
        received and a BindResponse has not yet been sent back.

        Note: there is currently no support for self-enrollment here!
        """
        if exempt_ous is None:
            exempt_ous = []
        super(DuoAutoLdapServer, self).__init__(**kwargs)
        self.primary_ator = primary_ator
        self.factors = factors
        self.failmode = failmode
        self.exempt_primary_bind = exempt_primary_bind
        self.exempt_ous = [DistinguishedName(str(dn).lower()) for dn in exempt_ous]
        self.no_more_binds = False
        self.allow_searches_after_bind = allow_searches_after_bind
        self.allow_unlimited_binds = allow_unlimited_binds
        self.resolving_bind_request = False
 def _is_under_exempt_ous(self, dn_str):
     if not self.exempt_ous:
         defer.returnValue(False)
     dn_str = dn_str.lower()
     try:
         dn = DistinguishedName(dn_str)
     except Exception:
         # Response DNs shouldn't be invalid but a request's DN
         # field may be a "NetBIOS domain\username". Implementing
         # exempt_ous for such binds requires an additional search
         # in order to get the DN.
         log.msg(
             "Bind Request did not have a full DN. Attempting to lookup full DN for {}"
             .format(dn_str))
         try:
             dn = yield self.username_to_dn(dn_str)
         except DnLookupFailed as e:
             log.msg(
                 "Failed to lookup full DN. Marking as non-exempt by default. Error: {}"
                 .format(e))
             defer.returnValue(False)
     defer.returnValue(
         any([base_dn.contains(dn) for base_dn in self.exempt_ous]))