LDAP Filters
This section of the SelfADSI tutorial deals with LDAP filters. The following contents are available here:
Definition of LDAP Filters
By using LDAP filters criteria can be specified when searching for certain objects within the directory. Nontechnically spoken, criteria for LDAP filters could be:
- All global groups of the domain.
- All users and contacts within a certain Organizational Unit (OU).
- All users whose postal codes start with a certain number and who haven't set the attribute 'displayName'.
- All users whose first names start with 'P'.
- All domain controllers.
- All users not shown in the address list, having their mailbox on a specific server.
- The server that holds the replica of a specific ADAM partition.
- The Active Directory site link for which a specific domain controller is bridgehead.
LDAP filters are needed in many situations. They can be used in several dialogs of the graphic AD or Exchange directory tools, either supported by wizards or entered directly by hand. An example for the use of an LDAP filter for a query that can be defined in the tool 'AD Users and Computers':
For ADSI scripting filters are mainly needed for the ADO search for objects in the directory.
LDAP filters are defined in the following RFCs (Request for Comments):
RFC 1588: A String Representation of LDAP Search Filters |
RFC 1960: A String Representation of LDAP Search Filters (replaces RFC 1588) |
RFC 2254: The String Representation of LDAP Search Filters (replaces RFC 1960) |
RFC 4515: Lightweight Directory Access Protocol (LDAP): String Representation of Search Filters (replaces RFC 2254) |
Syntax and Operators
LDAP filters consist of one or more criteria which can be linked together by using AND or OR operators. Hereby, the operators are always placed in front of the operands (i.e. the criteria). This is the so-called 'Polish Notation'. The search criteria have to be put in parentheses and then the whole term has to be bracketed one more time.
AND Operation:
(& (...K1...) (...K2...)) or
with more than two criteria: (& (...K1...)
(...K2...) (...K3...) (...K4...))
OR Operation:
(| (...K1...) (...K2...)) or with more than two criteria: (| (...K1...) (...K2...) (...K3...) (...K4...))
Nested Operation:
Every AND/OR operation can also be understood as a single criterion:
(|(& (...K1...) (...K2...))(& (...K3...) (...K4...))) means: (K1 AND K2) OR (K3 AND K4)
The search criteria consist of a requirement for an LDAP attribute, e.g. (givenName=Sandra). Following rules should be considered:
Equality: | (attribute=abc) , e.g. (&(objectclass=user)(displayName=Foeckeler) |
Negation: | (!(attribute=abc)) , e.g. (!objectClass=group) |
Presence: | (attribute=*) , e.g. (mailNickName=*) |
Absence: | (!(attribute=*)) , e.g. (!proxyAddresses=*) |
Greater than: | (attribute>=abc) , e.g. (mdbStorageQuota>=100000) |
Less than: | (attribute<=abc) , e.g. (mdbStorageQuota<=100000) |
Proximity: | (attribute~=abc) , e.g. (displayName~=Foeckeler) Caution: ~= is treated as = in AD environments !! |
Wildcards: | e.g. (sn=F*) or (mail=*@cerrotorre.de) or (givenName=*Paul*) |
Further rules:
Real attributes only........ | Only
standard attributes can be used for LDAP filters. When specifying an
LDAP search filter, you cannot use object properties of the ADSI objects
that aren't LDAP database attributes but interface properties of the
regarding object. A list of the affected properties can be viewed under
the topic 'Object
Properties of ADSI Objects'. |
||||||||||||||||||||||||||||||||||||||||||
No quotation marks........ | Comparative strings do NOT appear in quotation marks. A filter for
the displayName 'Philipp Foeckeler' would read as follows: (displayName=Philipp
Foeckeler). |
||||||||||||||||||||||||||||||||||||||||||
Upper/lower case.......... | If
you want to filter boolean attributes the consideration of the upper/
lower case will be crucial. The use of TRUE or FALSE is
absolutely necessary for filtering such booleans. However, most other
string attributes are case-insensitiv, i.e. a hit will be found even
if the upper and lower case differs from your search filter. Especially in Exchange 5.5 directories most of the attributes are case sensitive. There are only few exceptions which can be viewed under the topic 'Directory Attributes with CaseIgnoreString Syntax'. |
||||||||||||||||||||||||||||||||||||||||||
DN-String attributes....... |
Regarding
match algorithms of LDAP filters, LDAP directory systems comply with
the specifications of the original X.500 standards. According to these
matching rules you can't use wildcards in LDAP filters
for attributes containing LDAP distinguished names (attributes with
DN-string syntax / ADSI attribute data type ADSTYPE_DN_STRING = 1).
The same applies for ADS: Filters in which DN attributes are searched
with wildcards do not work. This can be quite irritating. You can't e.g. search for all users that are members in groups that contain a certain string in their group names. The reason for this is that the user attribute memberOf has the data type DN-string. Even more important could be the search for objects in a specific OU. Especially, when only the declaration of a pure filter string is allowed and when there is no possibility to specify the search base of an LDAP search. This might well be so e.g. for the definition of recipient policies in Microsoft Exchange environments. Thus, the following filter won't work! (distinguishedName=*,ou=Sydney,dc=cerrotorre,dc=org) In this case we have to use a script-based solution which provides a workaround for this LDAP filter limitation. |
||||||||||||||||||||||||||||||||||||||||||
Special characters.......... | LDAP
filters can be specified by unicode characters. You may, for example,
use German umlauts - if it makes sense (if the filtered attribute is
an unicode string). However, the characters ( ) & | = ! >
< ~ * / \ play a special role for the declaration of LDAP filters. If you search for or want to compare these characters within an attribute string, you'll have to use a prefixed backslash and the corresponding hexadecimal ASCII code:
An example: We want to retrieve all objects whose attributes "displayName" start with "*" : (displayName=\2a) The character zero (\00) may also be required occasionally. |
||||||||||||||||||||||||||||||||||||||||||
Multivalued attributes.... | It's
also possible to filter for certain values in multivalue attributes.
An example is the attribute objectClass. Due to the hierarchical structure
of the directory schema, an object will always be an instance of several
object classes. An Active Directory user e.g. is an object of the class types top, person, organizationalPerson und user.
Thus, a filter could be: (objectClass=user) However, you need to take into consideration that such filtering always costs more server performance than an ordinary 'one-dimensional' attribute search does. |
Filtering for Hex Numbers and Binary Values
Hex Numbers.................. | In cases where attributes of the type integer or long
integer are compared and filtered for specific hex numbers, the correspondent
decimal coded number has always to be used in the LDAP filter. An example:
If you look for local security groups in the Active Directory following two flags
will have to be set for the groupType attribute: ADS_GROUP_TYPE_LOCAL_GROUP (0x00000004) ADS_GROUP_TYPE_SECURITY_ENABLED (0x80000000) The addition of these values is the hex value 0x80000004, calculated in the decimal number 2147483652 - this has to be used in the LDAP filter: (groupType=2147483652) |
Binary Values ................ | It's a completely different thing if you want to compose filters
for attributes whose data types appear as binary hex values (the according
data type is often referred to as 'Octet String'). If you are going to filter for such binary attributes, it is mandatory to declare every single byte that has to be compared in hex code. For instance, if you search for objects with the attribute 'Inventory' which has the value 0x01AAF5EF, then the appropriate filter will have to read: (Inventory=\01\aa\f5\ef) Unfortunately, wildcard search is not allowed when searching for binary attributes! Usually, octet string LDAP attributes are difficult to handle by a VBScript and thus have to be converted into a more manageable data type. The accordant procedures can be learnt in the SelfADSI article 'Directory Attributes of type Octet String'. |
Filtering for Bit Fields
By using LDAP filters it's also possible to find objects for which a specific bit either is or is not set within a bit field. In this case, an strange looking syntax has to be followed:
<Attribute name>:<BitFilterRule-ID>:=<decimal comparative value>
There are exactly two BitFilterRule IDs: One for bit-wise AND comparisons and one for bit-wise OR comparisons:
LDAP_MATCHING_RULE_BIT_AND 1.2.840.113556.1.4.803
LDAP_MATCHING_RULE_BIT_OR 1.2.840.113556.1.4.804
For the AND filter, only objects are found that have the attribute matches the filter value for all the bits. In the OR filter, only one single filter bit which is set in the object attribute is neede for a match.
An example:
For the attribute 'groupType' following bit mask is important in Active Directory group
objects:
ADS_GROUP_TYPE_GLOBAL_GROUP
= 0x00000002
ADS_GROUP_TYPE_LOCAL_GROUP
= 0x00000004
ADS_GROUP_TYPE_UNIVERSAL_GROUP = 0x00000008
ADS_GROUP_TYPE_SECURITY_ENABLED = 0x80000000
A filter for universal groups has to search for those objects in whose
attributes the 4th least significant bit is set. This can be checked by
equating the attribute of the value 0x00000008 (this represents the 4th
bit) in an AND filter:
Caution: In LDAP filters the hex value of the bit filter must be decimal at this point! So if all security groups (and not the distribution groups) are to be found, it has to be filtered for the 8th bit (0x80000000 = 2147483648):
An example for an OR filter: We search all users which don't need a password (userAccountControl is set on 0x20 - 32) or whose passwords never expires (userAccountControl is set on 0x10000 = 65536). So we have to build a filter with the value 65568 (=65536 + 32):
Please note that bit-wise filtering is a much more complex procedure for a server. For this reason you should take into consideration the use of normal equity criteria. If looking e.g. for universal security groups, the two flags 0x80000000 and 0x00000008 can be added up and then be filtered for the according decimal value 2147483656:
Filtering with Ambiguous Name Resolution (ANR)
The Ambiguous Name Resolution is able to find users or contacts in ADS environments whose names are only partly known. In this case not only the object name but also the display name, first and last name as well as the diverse mail addresses are involved in the search. As an Outlook user you can have a look at the ANR filtering search by using e.g. the option 'Check names' for getting the best hit while searching for an address.
Which attributes are integrated exactly in the ANR search is specified by the attribute search flags in the directory schema. By doing so, a so-called ANR set of attributes is declared. Following attributes are part of the ANR set by default:
- Relative Distinguished Name (RDN), this
could be for example the values for cn=.... or ou=...
- givenName (first name)
- sn (last name)
- displayName (display name)
- legacyExchangeDN (after migrations the Exchange 5.5 directory name of the old mailbox is shown here)
- proxyAddresses (mail addresses)
- physicalDeliveryOfficeName (office address)
The syntax of ANR filters is as follows:
(anr=Philipp) or (anr=p f) or (anr=Foeck)
All these filters would find the user 'Foeckeler, Philipp'. The
second one is able to find 'Philipp Foeckeler' as well as 'Fritz Paul'.
This is because the ANR filter checks the first name and last name in both
directions.
Examples for LDAP Filters in Active Directory Environments
All users: (&(objectCategory=person)(objectClass=user)) All users (more complex, but also more effective): (sAMAccountType=805306368) All groups: (objectClass=group) All contacts: (objectClass=contact) All users und contacts: (objectClass=user) All security groups (local, global and universal): (groupType:1.2.840.113556.1.4.803:=2147483648) All empty groups: (&(objectClass=group)(!member=*)) All groups which were changed since Dec 31 2008: (&(objectClass=group)(whenChanged>=20081231000000.0Z)) All users which didnt logon since Dec 31 2008: (&(&(objectCategory=person)(objectClass=user))(lastLogonTimestamp<=128752108510000000)) All users with the account configuration 'Password never expires': (&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=65536)) All computer accounts which are disabled: (&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=2)) All objects which can't be deleted: (systemFlags:1.2.840.113556.1.4.803:=-2147483648) All objects which can't be renamed: (systemFlags:1.2.840.113556.1.4.803:=134217728) All users with mailboxes on Exchange server 'KUNGUR': (msExchangeHomeserverName=/o=MAILOrg/ou=First Administrative Group/cn=Configuration/cn=Servers/cn=KUNGUR) All users whose account is disabled: (&(objectCategory=person)(objectClass=user)(userAccountControl:1.2.840.113556.1.4.803:=2)) All hidden Exchange mail recipients: (msExchHideFromAddressLists=TRUE) All hidden Exchange mail recipients (without public folder objects): (&(msExchHideFromAddressLists=TRUE)(!objectClass=publicFolder)) All mail recipients with fax adress: (proxyAddresses=FAX:*) All domain controllers: (&(objectClass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192)) All global catalog servers (LDAP search in the configuration partition): (&(objectClass=nTDSDSA)(options:1.2.840.113556.1.4.803:=1)) |
Examples for LDAP Filters in the Exchange 5.5 Directory
All mailboxes (objectClass=organizationalPerson) All distribution lists: (objectClass=groupOfNames) All custom recipients: (objectClass=Remote-Address) All mailboxes of the server 'TRANGO' in site 'Site1' in the organisation 'MAIL': (Home-MTA=cn=Microsoft MTA,cn=TRANGO,cn=Servers,cn=Configuration,ou=Site1,o=MAIL=Remote-Address) All hidden Exchange mail recipients (requires authentication as 'cn=USER,dc=DOM,cn=admin'): (Hide-From-Address-Book=TRUE) All recipients whose display name starts with 'F': (cn=F*) |