def get_user_groups(user_dn, user_attrs, connection=None):
    if not connection:
        connection = get_connection()
    groups = dict()
    # get groups referred by group objects
    if lsettings.GROUP_MEMBERS_FIELD:
        if lsettings.GROUP_MEMBERS_USE_DN:
            search_filter = '(%s=%s)' % (lsettings.GROUP_MEMBERS_FIELD, escape_filter_chars(user_dn))
        elif hasattr(user_attrs, 'items') and user_attrs.get(lsettings.USER_ID_FIELD):
            search_filter = '(%s=%s)' % (lsettings.GROUP_MEMBERS_FIELD, escape_filter_chars(user_attrs[lsettings.USER_ID_FIELD][0]))
        else:
            search_filter = None
        if search_filter:
            results = ldap_search(lsettings.GROUP_SEARCH_SCOPE, search_filter, connection=connection)
            for group in results:
                groups[group['dn']] = group['attributes']
    # get groups referred by user object
    if lsettings.USER_GROUPS_FIELD and hasattr(user_attrs, 'items') and user_attrs.get(lsettings.USER_GROUPS_FIELD):
        for name in user_attrs[lsettings.USER_GROUPS_FIELD]:
            if lsettings.USER_GROUPS_USE_DN:
                results = ldap_search(name, lsettings.GROUP_LIST_FILTER, connection=connection)
            else:
                results = ldap_search(lsettings.GROUP_SEARCH_SCOPE, lsettings.GROUP_SEARCH_FILTER % dict(group=escape_filter_chars(name)), connection=connection)
            for group in results:
                if group['dn'] not in groups:
                    groups[group['dn']] = group['attributes']
    return groups
Beispiel #2
0
def profile(request, username=None):
    if not username:
        if request.user.is_authenticated:
            username = request.user.username
        else:
            raise Http404("No such user!")
    with Connection(LDAP_URL) as c:
        c.search(
            "ou=People,dc=csua,dc=berkeley,dc=edu",
            "(uid={})".format(escape_filter_chars(username)),
            attributes="gecos",
        )
        if len(c.entries) == 0:
            raise Http404("No such user!")

        realname = str(c.entries[0].gecos).split(",", 1)[0]
        c.search(
            "ou=Group,dc=csua,dc=berkeley,dc=edu",
            "(memberUid={})".format(escape_filter_chars(username)),
            attributes="cn",
        )
        groups = [str(entry.cn) for entry in c.entries]

    return render(
        request,
        "profile.html",
        {"username": username, "groups": groups, "realname": realname},
    )
Beispiel #3
0
def do_classes_search(q):
    c = LDAPConnection()
    result_dns = []
    q = escape_filter_chars(q)
    q = q.replace(escape_filter_chars('*'), '*')
    parts = q.split(" ")
    # split on each word
    i = 0
    for p in parts:
        exact = False
        logger.debug(p)
        if p.startswith('"') and p.endswith('"'):
            exact = True
            p = p[1:-1]

        if exact:
            logger.debug("Simple exact: {}".format(p))
            # No implied wildcard
            query = (("(&(|(tjhsstClassId={0})"
                      "(cn={0})"
                      ")(|(objectClass=tjhsstClass)))")).format(p)
        else:
            logger.debug("Simple wildcard: {}".format(p))
            if p.endswith("*"):
                p = p[:-1]
            if p.startswith("*"):
                p = p[1:]
            # Search for first, last, middle, nickname uid, with implied
            # wildcard at beginning and end
            query = (("(&(|(tjhsstClassId=*{0})"
                      "(tjhsstClassId={0}*)"
                      "(cn=*{0})"
                      "(cn={0}*)"
                      ")(|(objectClass=tjhsstClass)))")).format(p)

        logger.debug("Running LDAP query: {}".format(query))

        res = c.search(settings.CLASS_DN, query, None)
        new_dns = []
        # if multiple words, delete those that weren't in previous searches
        for row in res:
            dn = row["dn"]
            if i == 0:
                new_dns.append(dn)
            elif dn in result_dns:
                new_dns.append(dn)

        result_dns = new_dns
        i += 1

    # loop through the DNs saved and get actual user objects
    classes = []
    for dn in result_dns:
        result_class = Class(dn=dn)
        if result_class not in classes:
            classes.append(result_class)
    classes = sorted(classes, key=lambda c: str(c))
    return classes
Beispiel #4
0
 def get(cls, key):
     from ldap3.utils.conv import escape_filter_chars
     key_attr, _ = cls.keyMapping
     try:
         return cls.query('{key_attr}: {key}'.format(
             key_attr=escape_filter_chars(key_attr),
             key=escape_filter_chars(key)))[0]
     except IndexError:
         return None
Beispiel #5
0
    def get_adobject(self, queried_domain=str(), queried_sid=str(),
                     queried_name=str(), queried_sam_account_name=str(),
                     ads_path=str(), attributes=list(), custom_filter=str()):
        for attr_desc, attr_value in (('objectSid', queried_sid), ('name', escape_filter_chars(queried_name)),
                                      ('samAccountName', escape_filter_chars(queried_sam_account_name))):
            if attr_value:
                object_filter = '(&({}={}){})'.format(attr_desc, attr_value, custom_filter)
                break
        else:
            object_filter = '(&(name=*){})'.format(custom_filter)

        return self._ldap_search(object_filter, adobj.ADObject, attributes=attributes)
Beispiel #6
0
def do_classes_search(q):
    c = LDAPConnection()
    result_dns = []
    q = escape_filter_chars(q)
    q = q.replace(escape_filter_chars('*'), '*')
    parts = q.split(" ")
    # split on each word
    i = 0
    for p in parts:
        exact = False
        logger.debug(p)
        if p.startswith('"') and p.endswith('"'):
            exact = True
            p = p[1:-1]

        if exact:
            logger.debug("Simple exact: {}".format(p))
            # No implied wildcard
            query = (("(&(|(tjhsstClassId={0})" "(cn={0})" ")(|(objectClass=tjhsstClass)))")).format(p)
        else:
            logger.debug("Simple wildcard: {}".format(p))
            if p.endswith("*"):
                p = p[:-1]
            if p.startswith("*"):
                p = p[1:]
            # Search for first, last, middle, nickname uid, with implied
            # wildcard at beginning and end
            query = (("(&(|(tjhsstClassId=*{0})" "(tjhsstClassId={0}*)" "(cn=*{0})" "(cn={0}*)" ")(|(objectClass=tjhsstClass)))")).format(p)

        logger.debug("Running LDAP query: {}".format(query))

        res = c.search(settings.CLASS_DN, query, None)
        new_dns = []
        # if multiple words, delete those that weren't in previous searches
        for row in res:
            dn = row["dn"]
            if i == 0:
                new_dns.append(dn)
            elif dn in result_dns:
                new_dns.append(dn)

        result_dns = new_dns
        i += 1

    # loop through the DNs saved and get actual user objects
    classes = []
    for dn in result_dns:
        result_class = Class(dn=dn)
        if result_class not in classes:
            classes.append(result_class)
    classes = sorted(classes, key=lambda c: str(c))
    return classes
Beispiel #7
0
def search_hosts(attributes=None, **params):
    assert isinstance(g.openldap_session, OpenLDAPSession)
    condition_list = ['(objectClass=*)', '(objectClass=device)']

    if attributes is None:
        attributes = 'cn', 'ipHostNumber'

    if 'cn' in params:
        condition_list.append('(cn=%s)' % escape_filter_chars(params['cn']))
    if 'cnlike' in params:
        condition_list.append('(cn=*%s*)' % escape_filter_chars(params['cnlike']))

    condition_str = '(&' + ''.join(condition_list) + ')'
    return g.openldap_session.search_hosts(condition_str, attributes)
Beispiel #8
0
    def authenticate(self, environ, identity):
        logger = logging.getLogger('repoze.who')

        if 'login' not in identity:
            return

        with make_connection(self.url, self.bind_dn, self.bind_pass) as conn:
            if self.start_tls:
                conn.start_tls()

            if not conn.bind():
                logger.error('Cannot establish connection')
                return

            escaped_login = escape_filter_chars(identity['login'])
            search = \
                self.search_pattern % escaped_login
            conn.search(self.base_dn, search, self.search_scope)

            if len(conn.response) > 1:
                logger.error('Too many entries found for %s', search)
                return
            if len(conn.response) < 1:
                logger.warn('No entry found for %s', search)
                return

            dn = conn.response[0]['dn']
            # Ensure proper encoding of unicode passwords
            password = identity['password'].encode('utf-8')
            with make_connection(self.url, dn, password) as check:
                if not check.bind():
                    return
                save_userdata(identity, dn)
                return dn if self.ret_style == 'd' else identity['login']
 def by_username(self, username) -> 'User':
     result = self._query('(uid={username:s})'.format(
         username=escape_filter_chars(username)))
     if len(result) > 0:
         return result[0]
     else:
         return None
Beispiel #10
0
    def authenticate(self, environ, identity):
        logger = logging.getLogger('repoze.who')

        if 'login' not in identity:
            return

        with make_connection(self.url, self.bind_dn, self.bind_pass) as conn:
            if self.start_tls:
                conn.start_tls()

            if not conn.bind():
                logger.error('Cannot establish connection')
                return

            escaped_login = escape_filter_chars(identity['login'])
            search = \
                self.search_pattern % escaped_login
            conn.search(self.base_dn, search, self.search_scope)

            if len(conn.response) > 1:
                logger.error('Too many entries found for %s', search)
                return
            if len(conn.response) < 1:
                logger.warn('No entry found for %s', search)
                return

            dn = conn.response[0]['dn']
            # Ensure proper encoding of unicode passwords
            password = identity['password'].encode('utf-8')
            with make_connection(self.url, dn, password) as check:
                if not check.bind():
                    return
                save_userdata(identity, dn)
                return dn if self.ret_style == 'd' else identity['login']
Beispiel #11
0
def user_login():
    """Allow passhportd to handle login/passwords for users"""
    # Only POST data are handled
    if request.method != "POST":
        return utils.response("ERROR: POST method is required ", 405)

    # Simplification for the reading
    login = request.form["login"]
    password = request.form["password"]

    # Check for required fields
    if not login or not password:
        return utils.response("ERROR: The login and password are required ",
                              417)
    elif login != escape_filter_chars(login):
        return utils.response("ERROR: Bad input", 417)

    # Check data validity uppon LDAP/local/whatever...
    result = try_login(login, password)
    if result == "success":
        app.logger.info("Authentication ok for {}".format(login))
        # If the LDAP connection is ok, user can connect
        return utils.response("Authorized", 200)
    app.logger.warning("Authentication error for {} => ".format(login) +
                       str(result))
    return utils.response("Refused: " + str(result), 200)
Beispiel #12
0
def useruid(s, login):
    """Connect to a LDAP and check the uid matching the given field data"""
    uid = False
    c = Connection(s, config.LDAPACC, password=config.LDAPPASS, auto_bind=True)

    if c.result["description"] != "success":
        app.logger.error(
            "Error connecting to the LDAP with the service account")
        return False

    # Look for the user entry.
    if not c.search(
            config.LDAPBASE,
            "(" + config.LDAPFIELD + "=" + escape_filter_chars(login) + ")"):
        app.logger.error(
            "Error: Connection to the LDAP with service account failed")
    else:
        if len(c.entries) >= 1:
            if len(c.entries) > 1:
                app.logger.error("Error: multiple entries with this login. "+ \
                          "Trying first entry...")
            uid = c.entries[0].entry_dn
        else:
            app.logger.error("Error: Login not found")
        c.unbind()

    return uid
Beispiel #13
0
    def do_rename_computer(self, line):
        args = shlex.split(line)

        if len(args) != 2:
            raise Exception("Current Computer sAMAccountName and New Computer sAMAccountName required (rename_computer comp1$ comp2$).")

        current_name = args[0]

        new_name = args[1]

        self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(current_name), attributes=['objectSid', 'sAMAccountName'])
        computer_dn = self.client.entries[0].entry_dn
        
        if not computer_dn:
            raise Exception("Computer not found in LDAP: %s" % current_name)

        entry = self.client.entries[0]
        samAccountName = entry["samAccountName"].value
        print("Original sAMAccountName: %s" % samAccountName)

        print("New sAMAccountName: %s" % new_name)
        self.client.modify(computer_dn, {'sAMAccountName':(ldap3.MODIFY_REPLACE, [new_name])})
        
        if self.client.result["result"] == 0:
            print("Updated sAMAccountName successfully")
        else:
            if self.client.result['result'] == 50:
                raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])
            elif self.client.result['result'] == 19:
                raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])
            else:
                raise Exception('The server returned an error: %s', self.client.result['message'])
Beispiel #14
0
 def get_user_info(self, user=None):
     """
     Look up user info from LDAP
     :param user:
     :type user:
     :return:
     :rtype:
     """
     if any(attr in user.casefold() for attr in ["uid=", "cn="]):
         search_base = user
     else:
         search_base = self.LDAP_USER_BASE_DN
     try:
         try:
             self.conn.search(
                 search_base=search_base,
                 search_filter=self.LDAP_USER_FILTER.replace(
                     "{username}", escape_filter_chars(user)),
                 attributes=["*"],
             )
             if len(self.conn.entries) > 0:
                 data = json.loads(self.conn.entries[0].entry_to_json())
                 return data
         except Exception as e:
             traceback.print_exc(file=sys.stderr)
     except Exception as e:
         traceback.print_exc(file=sys.stderr)
Beispiel #15
0
def passwordNone():
    dn = "dc={}".format(escape_rdn(request.args['dc']))
    search_filter = "(user={})".format(escape_filter_chars(request.args['search']))

    srv = Server('servername', get_info=ALL)
    conn = Connection(srv, user='******', password=None)
    status, result, response, _ = conn.search(dn, search_filter)
Beispiel #16
0
 def authenticate(self, credentials, request):  # pylint: disable=unused-argument
     """Authenticate provided credentials"""
     if not self.enabled:
         return None
     attrs = credentials.attributes
     login = attrs.get('login')
     password = attrs.get('password')
     conn = self.get_connection()
     search = LDAPQuery(self.base_dn, self.login_query, self.search_scope,
                        (self.login_attribute, self.uid_attribute))
     result = search.execute(conn, login=escape_filter_chars(login))
     if not result or len(result) > 1:
         return None
     result = result[0]
     login_dn = result[0]
     try:
         login_conn = self.get_connection(user=login_dn, password=password)
         login_conn.unbind()
     except LDAPBindError:
         LOGGER.debug("LDAP authentication exception with login %r", login, exc_info=True)
         return None
     else:
         if self.uid_attribute == DN_ATTRIBUTE:
             return USER_DN_PREFIX.format(prefix=self.prefix,
                                          dn=login_dn)
         attrs = result[1]
         if self.login_attribute in attrs:
             return USER_ATTR_PREFIX.format(prefix=self.prefix,
                                            attr=attrs[self.uid_attribute][0])
         return None
Beispiel #17
0
    def resolve_samname(self, samname):
        """
        Resolve a SAM name in the GC. This can give multiple results.
        Returns a list of LDAP entries
        """
        out = []
        safename = escape_filter_chars(samname)
        with self.lock:
            if not self.addc.gcldap:
                if not self.addc.gc_connect():
                    # Error connecting, bail
                    return None
            logging.debug('Querying GC for SAM Name %s', samname)
            entries = self.addc.search(
                search_base="",
                search_filter='(sAMAccountName=%s)' % safename,
                use_gc=True,
                attributes=[
                    'sAMAccountName', 'distinguishedName', 'sAMAccountType'
                ])
            # This uses a generator, however we return a list
            for entry in entries:
                out.append(entry)

        return out
Beispiel #18
0
    def validatePrivileges(self, uname, domainDumper):
        # Find the user's DN
        membersids = []
        sidmapping = {}
        privs = {
            'create': False, # Whether we can create users
            'createIn': None, # Where we can create users
            'escalateViaGroup': False, # Whether we can escalate via a group
            'escalateGroup': None, # The group we can escalate via
            'aclEscalate': False, # Whether we can escalate via ACL on the domain object
            'aclEscalateIn': None # The object which ACL we can edit
        }
        self.client.search(domainDumper.root, '(sAMAccountName=%s)' % escape_filter_chars(uname), attributes=['objectSid', 'primaryGroupId'])
        user = self.client.entries[0]
        usersid = user['objectSid'].value
        sidmapping[usersid] = user.entry_dn
        membersids.append(usersid)
        # The groups the user is a member of
        self.client.search(domainDumper.root, '(member:1.2.840.113556.1.4.1941:=%s)' % escape_filter_chars(user.entry_dn), attributes=['name', 'objectSid'])
        LOG.debug('User is a member of: %s' % self.client.entries)
        for entry in self.client.entries:
            sidmapping[entry['objectSid'].value] = entry.entry_dn
            membersids.append(entry['objectSid'].value)
        # Also search by primarygroupid
        # First get domain SID
        self.client.search(domainDumper.root, '(objectClass=domain)', attributes=['objectSid'])
        domainsid = self.client.entries[0]['objectSid'].value
        gid = user['primaryGroupId'].value
        # Now search for this group by SID
        self.client.search(domainDumper.root, '(objectSid=%s-%d)' % (domainsid, gid), attributes=['name', 'objectSid', 'distinguishedName'])
        group = self.client.entries[0]
        LOG.debug('User is a member of: %s' % self.client.entries)
        # Add the group sid of the primary group to the list
        sidmapping[group['objectSid'].value] = group.entry_dn
        membersids.append(group['objectSid'].value)
        controls = security_descriptor_control(sdflags=0x05) # Query Owner and Dacl
        # Now we have all the SIDs applicable to this user, now enumerate the privileges of domains and OUs
        entries = self.client.extend.standard.paged_search(domainDumper.root, '(|(objectClass=domain)(objectClass=organizationalUnit))', attributes=['nTSecurityDescriptor', 'objectClass'], controls=controls, generator=True)
        self.checkSecurityDescriptors(entries, privs, membersids, sidmapping, domainDumper)
        # Also get the privileges on the default Users container
        entries = self.client.extend.standard.paged_search(domainDumper.root, '(&(cn=Users)(objectClass=container))', attributes=['nTSecurityDescriptor', 'objectClass'], controls=controls, generator=True)
        self.checkSecurityDescriptors(entries, privs, membersids, sidmapping, domainDumper)

        # Interesting groups we'd like to be a member of, in order of preference
        interestingGroups = [
            '%s-%d' % (domainsid, 519), # Enterprise admins
            '%s-%d' % (domainsid, 512), # Domain admins
            'S-1-5-32-544', # Built-in Administrators
            'S-1-5-32-551', # Backup operators
            'S-1-5-32-548', # Account operators
        ]
        privs['escalateViaGroup'] = False
        for group in interestingGroups:
            self.client.search(domainDumper.root, '(objectSid=%s)' % group, attributes=['nTSecurityDescriptor', 'objectClass'])
            groupdata = self.client.response
            self.checkSecurityDescriptors(groupdata, privs, membersids, sidmapping, domainDumper)
            if privs['escalateViaGroup']:
                # We have a result - exit the loop
                break
        return (usersid, privs)
Beispiel #19
0
    def validatePrivileges(self, uname, domainDumper):
        # Find the user's DN
        membersids = []
        sidmapping = {}
        privs = {
            'create': False, # Whether we can create users
            'createIn': None, # Where we can create users
            'escalateViaGroup': False, # Whether we can escalate via a group
            'escalateGroup': None, # The group we can escalate via
            'aclEscalate': False, # Whether we can escalate via ACL on the domain object
            'aclEscalateIn': None # The object which ACL we can edit
        }
        self.client.search(domainDumper.root, '(sAMAccountName=%s)' % escape_filter_chars(uname), attributes=['objectSid', 'primaryGroupId'])
        user = self.client.entries[0]
        usersid = user['objectSid'].value
        sidmapping[usersid] = user.entry_dn
        membersids.append(usersid)
        # The groups the user is a member of
        self.client.search(domainDumper.root, '(member:1.2.840.113556.1.4.1941:=%s)' % escape_filter_chars(user.entry_dn), attributes=['name', 'objectSid'])
        LOG.debug('User is a member of: %s' % self.client.entries)
        for entry in self.client.entries:
            sidmapping[entry['objectSid'].value] = entry.entry_dn
            membersids.append(entry['objectSid'].value)
        # Also search by primarygroupid
        # First get domain SID
        self.client.search(domainDumper.root, '(objectClass=domain)', attributes=['objectSid'])
        domainsid = self.client.entries[0]['objectSid'].value
        gid = user['primaryGroupId'].value
        # Now search for this group by SID
        self.client.search(domainDumper.root, '(objectSid=%s-%d)' % (domainsid, gid), attributes=['name', 'objectSid', 'distinguishedName'])
        group = self.client.entries[0]
        LOG.debug('User is a member of: %s' % self.client.entries)
        # Add the group sid of the primary group to the list
        sidmapping[group['objectSid'].value] = group.entry_dn
        membersids.append(group['objectSid'].value)
        controls = security_descriptor_control(sdflags=0x05) # Query Owner and Dacl
        # Now we have all the SIDs applicable to this user, now enumerate the privileges of domains and OUs
        entries = self.client.extend.standard.paged_search(domainDumper.root, '(|(objectClass=domain)(objectClass=organizationalUnit))', attributes=['nTSecurityDescriptor', 'objectClass'], controls=controls, generator=True)
        self.checkSecurityDescriptors(entries, privs, membersids, sidmapping, domainDumper)
        # Also get the privileges on the default Users container
        entries = self.client.extend.standard.paged_search(domainDumper.root, '(&(cn=Users)(objectClass=container))', attributes=['nTSecurityDescriptor', 'objectClass'], controls=controls, generator=True)
        self.checkSecurityDescriptors(entries, privs, membersids, sidmapping, domainDumper)

        # Interesting groups we'd like to be a member of, in order of preference
        interestingGroups = [
            '%s-%d' % (domainsid, 519), # Enterprise admins
            '%s-%d' % (domainsid, 512), # Domain admins
            'S-1-5-32-544', # Built-in Administrators
            'S-1-5-32-551', # Backup operators
            'S-1-5-32-548', # Account operators
        ]
        privs['escalateViaGroup'] = False
        for group in interestingGroups:
            self.client.search(domainDumper.root, '(objectSid=%s)' % group, attributes=['nTSecurityDescriptor', 'objectClass'], controls=controls)
            groupdata = self.client.response
            self.checkSecurityDescriptors(groupdata, privs, membersids, sidmapping, domainDumper)
            if privs['escalateViaGroup']:
                # We have a result - exit the loop
                break
        return (usersid, privs)
Beispiel #20
0
    def do_get_user_groups(self, user_name):
        user_dn = self.get_dn(user_name)
        if not user_dn:
            raise Exception("User not found in LDAP: %s" % user_name)

        self.search('(member:%s:=%s)' % (LdapShell.LDAP_MATCHING_RULE_IN_CHAIN,
                                         escape_filter_chars(user_dn)))
    def toggle_account_enable_disable(self, user_name, enable):
        UF_ACCOUNT_DISABLE = 2
        self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(user_name), attributes=['objectSid', 'userAccountControl'])

        if len(self.client.entries) != 1:
            raise Exception("Error expected only one search result got %d results", len(self.client.entries))

        user_dn = self.client.entries[0].entry_dn
        if not user_dn:
            raise Exception("User not found in LDAP: %s" % user_name)

        entry = self.client.entries[0]
        userAccountControl = entry["userAccountControl"].value

        print("Original userAccountControl: %d" % userAccountControl) 

        if enable:
            userAccountControl = userAccountControl & ~UF_ACCOUNT_DISABLE
        else:
            userAccountControl = userAccountControl | UF_ACCOUNT_DISABLE

        self.client.modify(user_dn, {'userAccountControl':(ldap3.MODIFY_REPLACE, [userAccountControl])})

        if self.client.result['result'] == 0:
            print("Updated userAccountControl attribute successfully")
        else:
            if self.client.result['result'] == 50:
                raise Exception('Could not modify object, the server reports insufficient rights: %s', self.client.result['message'])
            elif self.client.result['result'] == 19:
                raise Exception('Could not modify object, the server reports a constrained violation: %s', self.client.result['message'])
            else:
                raise Exception('The server returned an error: %s', self.client.result['message'])
Beispiel #22
0
def _maybe_escape_filter_chars(value):
    """Escape and return according to RFC04515 if type is string-like.

    Else, return the unchanged object.
    """
    if isinstance(value, type(b'')) or isinstance(value, type(u'')):
        return escape_filter_chars(value)
    return value
Beispiel #23
0
def _maybe_escape_filter_chars(value):
    """Escape and return according to RFC04515 if type is string-like.

    Else, return the unchanged object.
    """
    if isinstance(value, bytes) or isinstance(value, str):
        return escape_filter_chars(value)
    return value
Beispiel #24
0
 def test_parse_search_filter_parenteses(self):
     f = parse_filter(
         '(cn=' + escape_filter_chars('Doe (Missing Inc)') + ')', None,
         test_auto_escape, test_auto_encode, test_check_names)
     self.assertEqual(f.elements[0].tag, MATCH_EQUAL)
     self.assertEqual(f.elements[0].assertion['attr'], 'cn')
     self.assertEqual(f.elements[0].assertion['value'],
                      b'Doe \\28Missing Inc\\29')
Beispiel #25
0
 def get_sid_info(self, sid):
     self.ldap_session.search(self.domain_dumper.root, '(objectSid=%s)' % escape_filter_chars(sid), attributes=['samaccountname'])
     try:
         dn = self.ldap_session.entries[0].entry_dn
         samname = self.ldap_session.entries[0]['samaccountname']
         return dn, samname
     except IndexError:
         logging.error('SID not found in LDAP: %s' % sid)
         return False
Beispiel #26
0
 def get_user_info(self, samname):
     self.ldap_session.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(samname), attributes=['objectSid'])
     try:
         dn = self.ldap_session.entries[0].entry_dn
         sid = format_sid(self.ldap_session.entries[0]['objectSid'].raw_values[0])
         return dn, sid
     except IndexError:
         logging.error('User not found in LDAP: %s' % samname)
         return False
Beispiel #27
0
    def get_dn(self, sam_name):
        if "," in sam_name:
            return sam_name

        try:
            self.client.search(self.domain_dumper.root, '(sAMAccountName=%s)' % escape_filter_chars(sam_name), attributes=['objectSid'])
            return self.client.entries[0].entry_dn
        except IndexError:
            return None
Beispiel #28
0
 def getUserInfo(self, domainDumper, samname):
     entries = self.client.search(domainDumper.root, '(sAMAccountName=%s)' % escape_filter_chars(samname), attributes=['objectSid'])
     try:
         dn = self.client.entries[0].entry_dn
         sid = self.client.entries[0]['objectSid']
         return (dn, sid)
     except IndexError:
         LOG.error('User not found in LDAP: %s' % samname)
         return False
Beispiel #29
0
 def getUserInfo(self, domainDumper, samname):
     entries = self.client.search(domainDumper.root, '(sAMAccountName=%s)' % escape_filter_chars(samname), attributes=['objectSid'])
     try:
         dn = self.client.entries[0].entry_dn
         sid = self.client.entries[0]['objectSid']
         return (dn, sid)
     except IndexError:
         LOG.error('User not found in LDAP: %s' % samname)
         return False
Beispiel #30
0
 def scrub_dict(source, remove_empty: bool = False):
     target = {}
     for key in source:
         value = escape_filter_chars(source[key])
         if remove_empty and (value is None or value == 'None'
                              or len(value) == 0):
             continue
         target[key] = value
     return target
Beispiel #31
0
    def do_get_group_users(self, group_name):
        group_dn = self.get_dn(group_name)
        if not group_dn:
            raise Exception("Group not found in LDAP: %s" % group_name)

        self.search(
            '(memberof:%s:=%s)' % (LdapShell.LDAP_MATCHING_RULE_IN_CHAIN,
                                   escape_filter_chars(group_dn)),
            "sAMAccountName", "name")
Beispiel #32
0
def search_services(attributes=None, **params):
    assert isinstance(g.openldap_session, OpenLDAPSession)
    condition_list = ['(objectClass=*)', '(objectClass=authorizedServiceObject)']

    if attributes is None:
        attributes = 'cn', 'authorizedService'

    if 'cn' in params:
        condition_list.append('(cn=%s)' % escape_filter_chars(params['cn']))
    if 'cnlike' in params:
        condition_list.append('(cn=*%s*)' % escape_filter_chars(params['cnlike']))
    if 'cnin' in params:
        _cns = []
        for cn in params['cnin']:
            _cns.append('(cn=%s)' % escape_filter_chars(cn))
        condition_list.append('(|%s)' % ''.join(_cns))

    condition_str = '(&' + ''.join(condition_list) + ')'
    return g.openldap_session.search_services(condition_str, attributes)
Beispiel #33
0
def type_of_host(hostname):
    """Returns the type of a host as specified in LDAP.

    >>> type_of_host('eruption')
    'desktop'
    >>> type_of_host('supernova')
    'server'
    """
    hosts = hosts_by_filter('(cn={})'.format(escape_filter_chars(hostname)))
    return hosts[0]['type'] if hosts else None
Beispiel #34
0
 def __call__(self, form, field):
     try:
         user = User.get(form.username.data)
         if user and user.mail == field.data:
             return
     except AttributeError:
         pass
     from ldap3.utils.conv import escape_filter_chars
     if len(User.query('mail: {}'.format(escape_filter_chars(field.data)))) != 0:
         raise ValidationError('E-Mail address already in use')
Beispiel #35
0
    def authenticate(self, request, username=None, password=None):
        ldap_conn = connections['ldap']
        user = None
        username = conv.escape_filter_chars(username, encoding=None)
        lu = LdapAcademiaUser.objects.filter(uid=username).first()
        if not lu:
            logger.info("--- LDAP BIND failed for {} ---".format(username))
            return None

        # check if username exists and if it is active
        try:
            ldap_conn.connect()
            ldap_conn.connection.bind_s(lu.distinguished_name(),
                                        password)
            ldap_conn.connection.unbind_s()
        except Exception:
            logger.info(
                "--- LDAP {} seems to be unable to Auth ---".format(username))
            return None

        # if account beign unlocked this will be always false
        if not lu.is_active():
            logger.info(
                "--- LDAP {} seems to be disabled ---".format(username))
            return None

        # username would be like an EPPN
        username = lu.uid
        try:
            user = get_user_model().objects.get(username=username)
            # update attrs:
            user.email = lu.mail[0]
            user.first_name = lu.cn
            user.last_name = lu.sn
            user.origin = 'ldap_peoples'
            user.save()
        except Exception:
            user = get_user_model().objects.create(username=username,
                                                   email=lu.mail[0],
                                                   first_name=lu.cn,
                                                   last_name=lu.sn,
                                                   origin='ldap_peoples')

        # TODO: Create a middleware for this
        # disconnect already created session, only a session per user is allowed
        # get all the active sessions
        # if not settings.MULTIPLE_USER_AUTH_SESSIONS:
            # for session in Session.objects.all():
            # try:
            # if int(session.get_decoded()['_auth_user_id']) == user.pk:
            # session.delete()
            # except (KeyError, TypeError, ValueError):
            # pass

        return user
    def normalize_username(self, username):
        """
        Normalize username for ldap query

        modifications:
         - format to lowercase
         - escape filter characters (ldap3)
        """
        username = username.lower()
        username = escape_filter_chars(username)
        return username
Beispiel #37
0
    def _operator_predicate(self, name, op, value):
        name = self.mappings.get(name, name)
        if name is None:
            return

        if name is True:
            return '(objectClass=*)'

        if op == '=':
            return '({0}={1})'.format(name, escape_filter_chars(str(value)))

        if op == '~':
            return '({0}={1})'.format(name, str(value))

        if op == '!=':
            return '(!({0}={1}))'.format(name, escape_filter_chars(str(value)))

        if op == 'in':
            return self._joint_predicate('or', [
                (name, '=', str(v)) for v in value
            ])
 def escape_userdn_if_needed(self, userdn):
     if self.escape_userdn:
         return escape_filter_chars(userdn)
     else:
         return userdn
Beispiel #39
0
 def test_search_exact_match_with_escape_chars_backslash_in_filter(self):
     self.delete_at_teardown.append(add_user(self.connection, testcase_id, 'sea-15', attributes={'givenName': testcase_id + 'givenname\\-15'}))
     result = self.connection.search(search_base=test_base, search_filter='(givenname=' + testcase_id + '*' + escape_filter_chars('\\') + '*)', attributes=[test_name_attr, 'sn'])
     if not self.connection.strategy.sync:
         response, result = self.connection.get_response(result)
     else:
         response = self.connection.response
         result = self.connection.result
     self.assertEqual(result['description'], 'success')
     self.assertEqual(len(response), 1)
     if test_server_type == 'AD':
         self.assertEqual(response[0]['attributes'][test_name_attr], testcase_id + 'sea-15')
     else:
         self.assertEqual(response[0]['attributes'][test_name_attr][0], testcase_id + 'sea-15')
    def authenticate(self, handler, data):
        username = data['username']
        password = data['password']
        # Get LDAP Connection
        def getConnection(userdn, username, password):
            server = ldap3.Server(
                self.server_address,
                port=self.server_port,
                use_ssl=self.use_ssl
            )
            self.log.debug('Attempting to bind {username} with {userdn}'.format(
                    username=username,
                    userdn=userdn
            ))
            conn = ldap3.Connection(
                server,
                user=self.escape_userdn_if_needed(userdn),
                password=password,
                auto_bind=self.use_ssl and ldap3.AUTO_BIND_TLS_BEFORE_BIND or ldap3.AUTO_BIND_NO_TLS,
            )
            return conn

        # Protect against invalid usernames as well as LDAP injection attacks
        if not re.match(self.valid_username_regex, username):
            self.log.warn('username:%s Illegal characters in username, must match regex %s', username, self.valid_username_regex)
            return None

        # No empty passwords!
        if password is None or password.strip() == '':
            self.log.warn('username:%s Login denied for blank password', username)
            return None

        isBound = False
        self.log.debug("TYPE= '%s'",isinstance(self.bind_dn_template, list))

        resolved_username, userdn = self.resolve_username_and_userdn(username)
        if resolved_username is None:
            return None

        if self.lookup_dn:
            if str(self.lookup_dn_user_dn_attribute).upper() == 'CN':
                # Only escape commas if the lookup attribute is CN
                resolved_username = re.subn(r"([^\\]),", r"\1\,", resolved_username)[0]

        bind_dn_template = self.bind_dn_template

        if bind_dn_template == []:
            isBound = True
            conn = getConnection(userdn, username, password)
        else:
            if isinstance(bind_dn_template, str):
                # bind_dn_template should be of type List[str]
                bind_dn_template = [bind_dn_template]

            for dn in bind_dn_template:
                userdn = dn.format(username=resolved_username)
                msg = 'Status of user bind {username} with {userdn} : {isBound}'
                try:
                    conn = getConnection(userdn, username, password)
                except ldap3.core.exceptions.LDAPBindError as exc:
                    isBound = False
                    msg += '\n{exc_type}: {exc_msg}'.format(
                        exc_type=exc.__class__.__name__,
                        exc_msg=exc.args[0] if exc.args else ''
                    )
                else:
                    isBound = conn.bind()
                msg = msg.format(
                    username=username,
                    userdn=userdn,
                    isBound=isBound
                )
                self.log.debug(msg)
                if isBound:
                    break

        if isBound:
            if self.allowed_groups:
                self.log.debug('username:%s Using dn %s', username, userdn)
                for group in self.allowed_groups:
                    groupfilter = (
                        '(|'
                        '(member={userdn})'
                        '(uniqueMember={userdn})'
                        '(memberUid={uid})'
                        ')'
                    ).format(userdn=escape_filter_chars(userdn), uid=escape_filter_chars(username))
                    groupattributes = ['member', 'uniqueMember', 'memberUid']
                    if conn.search(
                        group,
                        search_scope=ldap3.BASE,
                        search_filter=groupfilter,
                        attributes=groupattributes
                    ):
                        return username
                # If we reach here, then none of the groups matched
                self.log.warn('username:%s User not in any of the allowed groups', username)
                return None
            elif self.search_filter:
                conn.search(
                    search_base=self.user_search_base,
                    search_scope=ldap3.SUBTREE,
                    search_filter=self.search_filter.format(userattr=self.user_attribute,username=username),
                    attributes=self.attributes
                )
                if len(conn.response) == 0:
                    self.log.warn('User with {userattr}={username} not found in directory'.format(
                        userattr=self.user_attribute, username=username))
                    return None
                elif len(conn.response) > 1:
                    self.log.warn('User with {userattr}={username} found more than {len}-fold in directory'.format(
                        userattr=self.user_attribute, username=username, len=len(conn.response)))
                    return None
                return username
            else:
                return username
        else:
            self.log.warn('Invalid password for user {username}'.format(
                username=username,
            ))
            return None
Beispiel #41
0
 def test_parse_search_filter_parenteses(self):
     f = parse_filter('(cn=' + escape_filter_chars('Doe (Missing Inc)') + ')', None, test_auto_escape, test_auto_encode, test_validator, test_check_names)
     self.assertEqual(f.elements[0].tag, MATCH_EQUAL)
     self.assertEqual(f.elements[0].assertion['attr'], 'cn')
     self.assertEqual(f.elements[0].assertion['value'], b'Doe \\28Missing Inc\\29')
Beispiel #42
0
def do_ldap_query(q, admin=False):
    c = LDAPConnection()
    result_dns = []

    q = escape_filter_chars(q)
    # Allow wildcards
    q = q.replace(escape_filter_chars('*'), '*')

    # If only a digit, search for student ID and user ID
    if q.isdigit():
        logger.debug("Digit search: {}".format(q))
        if USE_SID_LDAP:
            query = ("(&(|(tjhsstStudentId={0})" "(iodineUidNumber={0})" ")(|(objectClass=tjhsstStudent)(objectClass=tjhsstTeacher)))").format(q)

            logger.debug("Running LDAP query: {}".format(query))

            res = c.search(settings.USER_DN, query, None)
            for row in res:
                dn = row["dn"]
                result_dns.append(dn)
        else:
            sid_users = User.objects.filter(_student_id=q)
            uid_users = User.objects.filter(id=q)
            for u in sid_users:
                result_dns.append(u.dn)

            for u in uid_users:
                result_dns.append(u.dn)
            logger.debug("Running local DB query for UID and SID")
    elif ":" in q:
        logger.debug("Advanced search")
        # A mapping between search keys and LDAP entires
        map_attrs = {
            "firstname": (
                "givenname",
                "nickname",),
            "first": (
                "givenname",
                "nickname",),
            "lastname": ("sn",),
            "last": ("sn",),
            "nick": ("nickname",),
            "nickname": ("nickname",),
            "name": (
                "sn",
                "mname",
                "givenname",
                "nickname",),
            "city": ("l",),
            "town": ("l",),
            "middlename": ("mname",),
            "middle": ("mname",),
            "phone": (
                "homephone",
                "mobile",),
            "homephone": ("homephone",),
            "cell": ("mobile",),
            "address": ("street",),
            "zip": ("postalcode",),
            "grade": ("graduationYear",),
            "gradyear": ("graduationYear",),
            "email": ("mail",),
            "studentid": ("tjhsstStudentId",),
            "sex": ("sex",),
            "gender": ("sex",),
            "id": ("iodineUidNumber",),
            "username": ("iodineUid",),
            "counselor": ("counselor",),
            "type": ("objectClass",)
        }

        inner = ""
        parts = q.split(" ")
        # split each word
        for p in parts:
            # Check for less than/greater than, and replace =
            sep = "="
            if ":" in p:
                cat, val = p.split(":")
            elif "=" in p:
                cat, val = p.split("=")
            elif "<" in p:
                cat, val = p.split("<")
                sep = "<="
            elif ">" in p:
                cat, val = p.split(">")
                sep = ">="
            else:
                logger.debug("Advanced fallback: {}".format(p))
                # Fall back on regular searching (there's no key)

                # Wildcards are already implied at the start and end
                if p.endswith("*"):
                    p = p[:-1]
                if p.startswith("*"):
                    p = p[1:]

                exact = False
                if p.startswith('"') and p.endswith('"'):
                    exact = True
                    p = p[1:-1]

                if len(p) == 0:
                    continue

                if exact:
                    # No implied wildcard
                    inner += (("(|(givenName={0})"
                               "(sn={0})"
                               "(iodineUid={0})") + ("(mname={0})" if admin else "") + ("(nickname={0})"
                                                                                        ")")).format(p)
                else:
                    # Search firstname, lastname, uid, nickname (+ middlename if admin) with
                    # implied wildcard at beginning and end of the search
                    # string
                    inner += (("(|(givenName=*{0})"
                               "(givenName={0}*)"
                               "(sn=*{0})"
                               "(sn={0}*)"
                               "(iodineUid=*{0})"
                               "(iodineUid={0}*)") + ("(mname=*{0})"
                                                      "(mname={0}*)" if admin else "") + ("(nickname=*{0})"
                                                                                          "(nickname={0}*)"
                                                                                          ")")).format(p)

                continue  # skip rest of processing
            logger.debug("Advanced exact: {}".format(p))
            if val.startswith('"') and val.endswith('"'):
                # Already exact
                val = val[1:-1]

            cat = cat.lower()
            val = val.lower()

            # fix grade, because LDAP only stores graduation year
            if cat == "grade" and val.isdigit():
                val = "{}".format(Grade.year_from_grade(int(val)))
            elif cat == "grade" and val == "staff":
                cat = "type"
                val = "teacher"
            elif cat == "grade" and val == "student":
                cat = "type"
                val = "student"

            if cat == "type" and val == "teacher":
                val = "tjhsstTeacher"
            elif cat == "type" and val == "student":
                val = "tjhsstStudent"

            # replace sex:male with sex:m and sex:female with sex:f
            if cat == "sex" or cat == "gender":
                val = val[:1]

            # if an invalid key, ignore
            if cat not in map_attrs:
                continue

            attrs = map_attrs[cat]

            inner += "(|"
            # for each of the possible LDAP fields, add to the search query
            for attr in attrs:
                inner += "({}{}{})".format(attr, sep, val)
            inner += ")"

        query = "(&{}(|(objectClass=tjhsstStudent)(objectClass=tjhsstTeacher)))".format(inner)

        logger.debug("Running LDAP query: {}".format(query))

        res = c.search(settings.USER_DN, query, None)
        for row in res:
            dn = row["dn"]
            result_dns.append(dn)

    else:
        logger.debug("Simple search")
        # Non-advanced search; no ":"
        parts = q.split(" ")
        # split on each word
        i = 0
        for p in parts:
            exact = False
            logger.debug(p)
            if p.startswith('"') and p.endswith('"'):
                exact = True
                p = p[1:-1]

            if exact:
                logger.debug("Simple exact: {}".format(p))
                # No implied wildcard
                query = (("(&(|(givenName={0})"
                          "(sn={0})"
                          "(iodineUid={0})") + ("(mname={0})"
                                                if admin else "") + ("(nickname={0})"
                                                                     ")(|(objectClass=tjhsstStudent)(objectClass=tjhsstTeacher)))")).format(p)
            else:
                logger.debug("Simple wildcard: {}".format(p))
                if p.endswith("*"):
                    p = p[:-1]
                if p.startswith("*"):
                    p = p[1:]
                # Search for first, last, middle, nickname uid, with implied
                # wildcard at beginning and end
                query = (("(&(|(givenName=*{0})"
                          "(givenName={0}*)"
                          "(sn=*{0})"
                          "(sn={0}*)"
                          "(iodineUid=*{0})"
                          "(iodineUid={0}*)") + ("(mname=*{0})"
                                                 "(mname={0}*)"
                                                 if admin else "") + ("(nickname=*{0})"
                                                                      "(nickname={0}*)"
                                                                      ")(|(objectClass=tjhsstStudent)(objectClass=tjhsstTeacher)))")).format(p)

            logger.debug("Running LDAP query: {}".format(query))

            res = c.search(settings.USER_DN, query, None)
            new_dns = []
            # if multiple words, delete those that weren't in previous searches
            for row in res:
                dn = row["dn"]
                if i == 0:
                    new_dns.append(dn)
                elif dn in result_dns:
                    new_dns.append(dn)

            result_dns = new_dns
            i += 1

    # loop through the DNs saved and get actual user objects
    users = []
    for dn in result_dns:
        user = User.get_user(dn=dn)
        if user.is_active:
            users.append(user)

    return users
def get_user_info(username, connection=None):
    results = ldap_search(lsettings.USER_SEARCH_SCOPE, lsettings.USER_SEARCH_FILTER % dict(user=escape_filter_chars(username)), connection=connection)
    if not results:
        raise Exception(str(_('User not found.')))
    if len(results) > 1:
        logger.warning('Multiple results found in LDAP server for search:\n%s\n%s\n%s', lsettings.USER_SEARCH_SCOPE, 'ldap3.SUBTREE', lsettings.USER_SEARCH_FILTER % dict(user=username))
    return results[0]['dn'], results[0]['attributes']