The Pain of LDAP
Tags: activedirectory, ldap, query, search
Published: 2018-08-02 18:28:50

---
title: The Pain of LDAP
published: true
tags: Active Directory,LDAP,query,search
canonical_url: https://thjones2.blogspot.com/2018/08/the-pain-of-ldap.html
---

Recently, we've had a push to enable 2FA on all of the internet-facing services we offer. A manager asked, "how many users do we have to worry about?" We have three basic types of users:

- Those who only have access to the Remote Desktops
- Those who only have access to the various applications
- Those who have access to both the Remote Desktops and to the various appliations

All such access is unprivileged. All accesses are managed via Active Directory group memberships.

- Our RDSH users are in a "remote desktops" group.
- Our application users are in a mix of groups. Fortunately, we long ago implemented standard naming for our groups. All group-names for one application-class start with "`T_`"; all group-names for the other application-class start with "`E_`"

Fortunately, the above means that I could get an answer for the manager by way of LDAP queries. A user's membership in an Active Directory is conveyed via the user-object's meberOf attribute. This means that the basic query-objects I needed to consider were:

- _RDSH Users:_ In LDAP-speak, this works out to `CN=<RDSH_GROUP>,cn=users,dc=<F>,dc=<Q>,dc=<D>,dc=<N>`
- _"T\_" Application-group:_ In LDAP-speak, this works out to `CN=T_*`
- _"E\_" Application group:_ In LDAP-speak, this works out to `CN=E_*`

LDAP queries generally operate on object-sets. Sets are denoted by parentheses. Using the previously-mentioned groups, a single-element set for each of my group-memberships would be:

- `(memberof=CN=<RDSH_GROUP>,cn=users,dc=<F>,dc=<Q>,dc=<D>,dc=<N>)`
- `(memberof=CN=T_*)`
- `(memberof=CN=E_*)`

Where things get _really freaking ugly_ is when you need to combine these into appropriate queries. Combining query-objects is a matter of using the booleans:


- "&": the logical AND operator
- "|": the logical OR operator
- "!": the logical NOT operator
- _If you're familiar with logical operators, you'll notice that there isn't a built in eXclusive OR type of operator._

Why I say things are ugly is that you apply a given operator within a set and the syntax is a little non-intutive:

- ANDing sets: Uses the syntax `(&(SET1)(SET2)(...)(SET_N))`. Basically, this says, "AND all of the following sets within this AND-set. An AND-set can consist of 2+ sets for evaluation
- ORing sets: Similarly, this uses the syntax `(|(SET1)(SET2)(...)(SET_N))`. Basically, this says, "OR all of the following sets within this OR-set. An OR-set can consist of 2+ sets for evaluation
- NOTin sets: Similarly, this uses the syntax `(!(SET1)(SET2)(...)(SET_N))`. Basically, this says, "OR all of the following sets within this NOT-set. An NOT-set can consist of 2+ sets for evaluation

Where it gets really ugly is when a logical operation acually requires two or more sub operations. For example:

- When you want to tell if a user is a member of one group but _not_ another, you need to do something like: `(&(GROUP1)(!(GROUP2))`
- Similarly, when you want to tell if a user is a member of one group but _not_ a member of both of two other groups, you would do something like `(&(GROUP1)(!(&(GROUP2)(GROUP3))`
- Similarly, when you want to tell if a user is a member of one group but _not_ a member of _either_ of two other groups, you would do something like `(&(GROUP1)(!(|(GROUP2)(GROUP3))`

As you can probably tell, as the number of selectors goes up, so does the ugliness.



At any rate, back to my original queries:

- Getting a list of all people with RDSH access, my query looks like: `(memberof=CN=<RDSH_GROUP>,cn=users,dc=<F>,dc=<Q>,dc=<D>,dc=<N>)`
- Getting a list of people with membership in either of the application groups, my query looks like: `(|(memberof=CN=T_*)(memberof=CN=T_*))`
- Getting a list of people with RDSH access and with membership in either of the application groups, my query looks like: `(&(memberof=CN=<RDSH_GROUP>,cn=users,dc=<F>,dc=<Q>,dc=<D>,dc=<N>)(|(memberof=CN=T_*)(memberof=CN=T_*)))`
- Getting a list of people with RDSH access but not access to either of the other groups , my query now looks like: `(&(memberof=CN=<RDSH_GROUP>,cn=users,dc=<F>,dc=<Q>,dc=<D>,dc=<N>)(!(|(memberof=CN=T_*)(memberof=CN=T_*))))`
- Conversely, getting a list of people without RDSH access but with access to either of the other groups, my query now looks like: `(&(!(memberof=CN=<RDSH_GROUP>,cn=users,dc=<F>,dc=<Q>,dc=<D>,dc=<N>))(|(memberof=CN=T_*)(memberof=CN=T_*)))`

All that ugliness aside, I _was_ able to get the manager the answer to his question and was able to do it quickly. This post is mostly a result of him asking "what the hell does all that gobbledygook mean" when I sent him the queries I ran to get him the answer. I'd sent that as part of the thread for the same reason I'm writing this: so I have the queries the next time the information is asked for again (and don't have to re-work it all out in my head again).