LDAP Search Filters

By | 2018-07-15

LDAP Search filters are fairly simple to learn and use. I am going to explain the filters in just enough depth so you can effectively use them. This guide is not going to be an exhaustive reference. If that is what you are looking for, then you want RFC 4515.

LDAP Search filters start with a (, followed by either a filter component, or one of three operators and operand(s), and end with a ).

For example, they will look something like this:

(Operator(filter)(filter)(filter)...)

or this:

(attr=value)

Let’s go through some real examples.

Simple Filter

(uid=tyler)

This matches all entries that have a uid attribute with a value of a tyler. If you are dealing with numeric values, you can also use >= or <=. As you would expect, these are greater than or equal to and less than or equal to, respectively. Keep in mind that if you try to use them with an attribute where this type of comparison doesn’t make sense, they will generally not match any entries.

The * Character

The * is used in two different types of filters. They are substring filters and presence filters. They look a lot alike, but they are NOT the same. Substrings only apply to string type values such as names and email addresses. Presence filters apply to attributes with any type.

Substrings

(mail=*@tylersguides.com)

The * is a wild card that will match against any number of any character. As you can probably infer, the example will match any entry with a mail attribute ending in @tylersguides.com. There is a catch though. If you try to use * with an attribute that is not a string, such as a numeric type, it will not match ANY entries. For example, if you try to use 200* to match against gidNumber, it will not match anything, even entries with a gidNumber of 2000.

Presence Filters

Unlike substrings, presence filters work with any type of value. A presence filter is used to find entries that have an attribute present. In other words, if the right side of an = has just a *, then that will match all entries that have the attribute on the left side. The following filter will match all entries that have a mail attribute.

(mail=*)

The table below should help clarify how the * works.

EXAMPLE WILL MATCH BUT NOT
abc* abc, abcd, abce ab, abb
*abc abc, 1abc, 12abc abcd, 1abcd
*abc* abc, 1abc, abcd ab, 123ab, ab1c23
a*c ac, abc, abbc, abbbc acd, 1ac
* Any entry with the attribute present Entries that don’t have the respective attribute.

The | Operator

(|(gidNumber>=2000)(mail=*@tylersguides.com))

The | is the logical or operator. You can also look at it as the any operator. This means that if any filters that follow it match an entry, you will have a match. The example above will match all entries with a gidNumber greater than or equal to 2000 or that have a mail attribute that ends with @tylersguides.com. It would match the following two entries:

dn: cn=testgroup,ou=groups,dc=tylersguides,dc=com
objectClass: posixGroup
cn: testgroup
gidNumber: 5000
memberUid: testuser

dn: uid=testuser,ou=users,dc=tylersguides,dc=com
objectClass: posixAccount
objectClass: inetOrgPerson
uidNumber: 5000
uid: testuser
mail: testuser@tylersguides.com
gidNumber: 1000
loginShell: /bin/sh
homeDirectory: /home/testuser
cn: Test
sn: Test

You can give | as many filters as you like. So the following filter is perfectly valid:

(|(gidNumber=5000)(uid=testuser)(memberUid=tyler)(loginShell=/bin/bash))

The ! Operator

(!(gidNumber=2000))

The ! is used to negate the filter it is being applied to. In other words, it will match all entries the filter does not apply to. The example will match any entry that either does not have a gidNumber attribute, or any entry that has a gidNumber that is not 2000. Unlike the other operators, the ! operator can only be applied to a single operand.

The & Operator

(&(gidNumber=5000)(mail=*))

The & operator is a logical and. This means that all of the filters must match. In the example, only entries with a gidNumber of 5000 that have a mail attribute will match the filter. Consider the following two entries.

dn: cn=testgroup,ou=groups,dc=tylersguides,dc=com
objectClass: posixGroup
cn: testgroup
gidNumber: 5000
memberUid: testuser

dn: uid=testuser,ou=users,dc=tylersguides,dc=com
objectClass: posixAccount
objectClass: inetOrgPerson
uidNumber: 5000
uid: testuser
mail: testuser@tylersguides.com
gidNumber: 5000
loginShell: /bin/sh
homeDirectory: /home/testuser
cn: Test
sn: Test

Only the second entry would match the filter. The first has a gidNumber of 5000, but it does not have a mail attribute.

As with the |, you can pass & an arbitrary number of operands.

Nesting Filters

The operands |, &, and ! work on are just filters, so you can nest operators. For example:

(!(|(gidNumber=4000)(uid=testuser)))

Consider only (|(gidNumber=4000)(uid=testuser)) for a bit. This will match any entry with gidNumber of 4000 or a uid of testuser. Outside if it, we have the ! operator. This means that the filter matches everything that (|(gidNumber=4000)(uid=testuser)) does NOT match. Perhaps a list of criteria will clarify it a bit. An entry will match the example filter if ANY of the following apply:

  • Entries that have neither a gidNumber nor a uid attribute.
  • Entries that have a gidNumber of anything except 4000 and no uid attribute
  • A missing gidNumber attribute and a uid that is anything except testuser
  • Entries that have a gidNumber that is not 4000 and a uid that is not testuser

When you are nesting an & or a | inside of a !, another way to think about it is to flip & and |, and put a ! in front of the operands. That sounds confusing, so consider that

(!(|(gidNumber=4000)(uid=testuser)))

is equivalent to:

(&(!(gidNumber=4000))(!(uid=testuser)))

You can nest operators as deeply as you need to. If you want to, you could do something as complicated as this:

(!(&(!(gidNumber=4000))(!(uid=testuser))(|(dn=*)(cn=*))))

but I generally try to keep things as simple as possible.

Summary

This is pretty much what you need to know to be able to use LDAP filters effectively. There are some things I intentionally left out, but as I wrote earlier, this isn’t meant to be a complete reference. This guide should be enough to configure filters on applications that use LDAP for user management. You should also be able to search directories with tools that use filters, such as the OpenLDAP ldapsearch program. That should be enough for most people. If you need more and have a pretty good handle on this material, you shouldn’t have too much trouble going deeper.

If you want to dig deeper, ldap.com explains extensible filters. The LDAP filter standard is defined by RFC 4515. I would also check your directory server documentation, since there may be things that are implementation specific, such as the ~=.