Investigating complex LDAP filters in Exchange

Customers migrating from Exchange 2003 to 2007 or 2010 often use my ConvertFrom-LdapFilter script to do very literal conversions from their old LDAP filters to the new OPATH filter syntax. In most cases, that works, but sometimes you’ll run across a filter like this:

(&(&(&(|(&(objectCategory=person)(objectSid=*)(!samAccountType:1.2.840.113556.1.4.804:=3))(&(objectCategory=person)(!objectSid=*))(&(objectCategory=group)(groupType:1.2.840.113556.1.4.804:=14))))(objectCategory=user)(memberOf=CN=SomeGroup,CN=Users,DC=contoso,DC=com)))

My script will refuse to process this filter because of the bitwise filters on samAccountType and groupType. So what to do?

The first thing you should do is step back and say, “Who do I want this filter to match? What was the original intent of this filter?” If you’re not sure, you may be able to get an idea by breaking down the filter logic like this:

(&
(&
(&
(|
(&
(objectCategory=person)
(objectSid=*)
(!samAccountType:1.2.840.113556.1.4.804:=3)
)
(&
(objectCategory=person)
(!objectSid=*)
)
(&
(objectCategory=group)
(groupType:1.2.840.113556.1.4.804:=14)
)
)
)
(objectCategory=user)
(memberOf=CN=SomeGroup,CN=Users,DC=contoso,DC=com)
)
)

Now that we can see how all the matching conditions relate to each other, we have a couple of questions to answer before we can totally understand the filter. We need to know what these two conditions mean:

(!samAccountType:1.2.840.113556.1.4.804:=3)
(groupType:1.2.840.113556.1.4.804:=14)

Searching the KB, we can find that 1.2.840.113556.1.4.804 is the bitwise OR match condition, described in KB 269181. This means that if any one of the specified flags is found, it’s a match.

Looking at the samAccountType filter, we have a value of 3 in decimal. Converting this to binary shows that we have the two rightmost bits set:

00000011

So this bitwise OR filter matches on the 0x1 bit or the 0x2 bit. But the condition starts with a NOT (that’s what the ! means), so we’ll only match objects where neither of those bits are set. Now we need to look up what all these bits mean for the samAccountType filter. We can find those definitions here: https://msdn.microsoft.com/en-us/library/cc228417(v=PROT.13).aspx. A quick look at the list reveals that if we’re not matching anything with 0x1 or 0x2, that leaves 3 values that we will match on:

SAM_GROUP_OBJECT
SAM_ALIAS_OBJECT
SAM_USER_OBJECT

So basically, the whole purpose of that condition is to say, “only match on groups, contacts, or users”.

Now that we have that part figured out, we need to find out what this groupType condition means. Again, we have a bitwise OR. This time the flags are 14 in decimal, which gives us the following bits:

00001110

The 0x2, 0x4, and 0x8 bit are set. We can find the definitions of the groupType values here: https://msdn.microsoft.com/en-us/library/cc228506(v=PROT.13).aspx. This tells us that this condition matches groups that are either Universal, Resource, or Account groups. I have no idea what these flags mean (aside from Universal), but I don’t have a single group in my lab that isn’t one of these types.

Now that we understand the whole filter, we can explain the logic like this:

  • The object must match any one of the following conditions:
    • It has a SID and is either a user, group, or contact
    • It does not have a SID
    • It is a group that has either the Universal, Resource, or Account flag set
  • The object must also match both of the following conditions:
    • objectCategory is user
    • It’s a member of the specified group

At this point, it’s pretty clear that the whole idea of this filter is just to match members of a group. The filter is extremely overcomplicated, and the bitwise filters that we had to spend so much time investigating are largely pointless. This filter was almost certainly built using Exchange System Manager in Exchange 2000 or 2003. That management tool was infamous for its overly-complex filters, and it’s unlikely that a human would purposely make a filter this obtuse. If I was writing my own LDAP filter to accomplish this in Exchange 2003, I would simply use:

(&(mailnickname=*)(memberOf=CN=SomeGroup,CN=Users,DC=contoso,DC=com))

But now that we know what the filter is supposed to do, we can just replace it directly with a much more elegant OPATH filter and call it a day:

memberOfGroup -eq CN=SomeGroup,CN=Users,DC=contoso,DC=com

Setting the RecipientFilter on the policy to that value will essentially accomplish the same thing as that huge, messy LDAP filter, and it’s a whole lot easier to read and quickly ascertain what it does.

For more on translating LDAP filters to OPATH, see this article: https://technet.microsoft.com/en-us/library/cc164375.aspx.