= Locating objects

Once the correct xref:modules/ldap/ldapsearch/connection_parameters.adoc[connection parameters]
 have been determined, the next step in building your configuration
is to locate where user and group objects are stored in the directory.

== How to locate user objects
Users are represented by a fairly limited subset of ObjectClasses.  The
following filters are usually sufficient to identify users in different
directory types.

- OpenLDAP - `(|(ObjectClass=organizationalPerson)(ObjectClass=posixAccount)(ObjectClass=Person))`
- ActiveDirectory - `(|(ObjectClass=User)(ObjectClass=posixAccount))`
- Novell eDir - `(ObjectClass=User)`

[TIP]
====
You may want to perform user searches bound as the 'root' or 'admin' user as
readonly service accounts may not have access to sensitive attributes like
`userPassword`.

If you discover that `userPassword` is only available when binding as 'root' or
'admin', you should request a privileged account for FreeRADIUS to use. This
account should still only have readonly access, but should have sufficient
privilledges to retrieve sensitive attributes like `userPassword.`
====

.Performing a search with a user object filter
====

[source,ldif]
----
ldapsearch -z 10 -x -H ldap://localhost:389 -b "dc=example,dc=com" "(|(ObjectClass=organizationalPerson)(ObjectClass=PosixAccount)(ObjectClass=Person))"

# extended LDIF
#
# LDAPv3
# base <dc=example,dc=com> with scope subtree
# filter: (|(ObjectClass=organizationalPerson)(ObjectClass=PosixAccount)(ObjectClass=Person))
# requesting: ALL
#

...

# doctopus, octopuses, example.com
dn: uid=doctopus,ou=octopuses,dc=example,dc=com <1>
objectClass: person                <2>
objectClass: organizationalPerson  <2>
objectClass: inetOrgPerson         <2>
cn: Doctopus McTentacles
dialupAccess: true  <3>
sn: McTentacles
uid: dpus           <4>
givenName: Doctopus
userPassword:: MGN0MHB1NTNzUnVsMw== <5>

# search result
search: 3
result: 0 Success

# numResponses: 18
# numEntries: 17
----

.Interpreting the result
<1> The Distinguished Name (DN) of the user object.
<2> Object classes the user object belongs to.
<3> An account enablement attribute.
    In this directory the `dialupAccess` attribute is being used to indicate if
    an account is enabled or disabled.
<4> The UID of the user.  This is the attribute containing the identity used
    when the user logs in to services.
<5> A base64 encoded `userPassword` attribute.  This shows the LDAP directory
    is willing to provided user password hashes.  These hashes can be used by
    the xref:raddb:mods-available/pap.adoc[pap] module to authenticate the
    user locally without the overhead of additional bind operations.
====

=== What to record

- `user_base_dn` - A DN of an object higher in the tree than relevant users
   object(s).
- `user_filter`- The filter which matches the objectClass(es) of user objects.
- `user_uid_attribute` - The attribute used to identify the user
   (usually `uid`, but can vary considerably between instances).
- `user_access_disabled_attribute` - Any attributes used to indicate whether an
   account is disabled. To determine if this attribute exists, repeat the user
   search (above) with a filter for a user account known to be disabled e.g.
   `(uid=a-disabled-user)`.
- `user_access_enabled_attribute` - Any attributes used to indicate whether an
   account is enabled.  Should be present in the search results already obtained.
- `user_password_attribute` - The attribute used to hold password data (if
   present).


.Locating users in "mature" LDAP directories
****
The above result represents an ideal scenario.  In reality, LDAP directories
often accumulate a lot of cruft.  Users objects may be located in
multiple container objects, and at multiple levels within a directory.

If you believe this to be the case with your directory, remove the `-z` argument
limiting the number of result, and pipe the output of `ldapsearch` through a
command chain _such as_: `grep dn: | sed -e 's/dn: [^,]*,//' | sort | uniq -c`.

This operation will return a list of objects which _contain_ user objects,
and how many user objects they contain.

.Determining which objects contain users
====
```
ldapsearch -x -H ldap://localhost:389 -b "dc=example,dc=com" "(|(ObjectClass=organizationalPerson)(ObjectClass=PosixAccount)(ObjectClass=Person))" | grep dn: | sed -e 's/dn: [^,]*,//' | sort | uniq -c
2 ou=octopuses,dc=example,dc=com
10 ou=people,dc=example,dc=com
1 ou=people,dc=created-by-someone-who-didnt-know-what-dc-meant,dc=example,dc=com
2 ou=people,ou=remnant-of-an-ancient-edir-instance,dc=example,dc=com
2 ou=giraffes,dc=example,dc=com
```
====

The user object base DN selected for your rlm_ldap configuration must be higher
in the object tree than the objects containing users.  In the above example the
user object base DN would be `dc=example,dc=com`.

If an error message is returned similar to `Size limit exceeded (4)`, it means
that the LDAP server is refusing to provide the complete set of results. If the
LDAP directory supports result paging, adding a paging request such as `-E
'pr=100'` may negate the per-search result limit.

For large result sets you should also specify a list of attributes to retrieve
after the search filter.  Limiting the attributes returned reduces load on the
LDAP server, and generally results in a quicker response.

```
ldapsearch ... -E 'pr=100' "(|(ObjectClass=...))" "dn" | ...
```
****

== How to locate group objects

Groups are represented by a fairly limited subset of ObjectClasses.  The
following filters are usually sufficient to identify groups in different
directory types.

- OpenLDAP - `(objectClass=groupOfNames)`
- ActiveDirectory - `(objectClass=group)`
- Novell eDir - `(objectClass=group)`

=== Group membership scheme variants
Groups memberships can be represented in multiple ways.  There are four main
variants:

1. User objects which reference groups by DN, _usually_ with multiple
instances of the `memberOf` attribute.
2. User objects which reference groups by name, again, _usually_
with multiple instances of the `memberOf` attribute.
3. Group objects which reference their members by DN, _usually_ with multiple
instances of the `member` attribute.
4. Group objects which reference their members by name, _usually_ with
multiple instances of the `memberUid` attribute.

In order to perform efficient group lookups which group membership scheme the
LDAP server uses must be determined.

Repeat the search for user objects using a filter that matches a user known
to be members of one or more groups e.g.
`(&(objectClass=posixAccount)(uid=doctopus))`.

- If the result shows a attribute containing the DN of known group, the LDAP
server implements _variant 1_.

- If the result shows a attribute containing the name of known group, the LDAP
server implements _variant 2_.

Perform the the search for group objects (below) using a filter that matches a group
the user is known to be a member of e.g. `(&(objectClass=groupOfNames)(cn=mariner-alert))`.

- If the result shows an attribute containing the DN of a user, the ldap server
implements _variant 3_.

- If the result shows an attribute containing the name of a user, the ldap server
implements _variant 4_.

.Performing a search with a group object filter
====
[source,ldif]
----
ldapsearch -z 10 -x -H ldap://localhost:389 -b "dc=example,dc=com" "(objectClass=groupOfNames)" "*" "+"

# extended LDIF
#
# LDAPv3
# base <ou=people,dc=example,dc=com> with scope subtree
# filter: (ObjectClass=posixGroup)
# requesting: ALL
#

...

dn: cn=mariner-alert,ou=groups,dc=example,dc=com <1>
cn: mariner-alert  <2>
description: Heads up to all delicious denizens of the directory
member: uid=doctopus,ou=octopuses,dc=example,dc=com  <3>
member: uid=rocktopus,ou=octopuses,dc=example,dc=com <3>
objectClass: groupOfNames <4>
objectClass: fdGroupMail  <4>
mail: mariner-alert@example.com

# search result
search: 3
result: 0 Success

# numResponses: 10
# numEntries: 9
----

.Interpreting the result

<1> The Distinguished Name (DN) of the group object.
<2> The name of the group stored in the `cn` attribute.
<3> Member attributes pointing to user objects by DN.  Indicates
    this directory implements group membership variant 3.
<4> Object classes the group object belongs to.

====

=== What to record

* `group_base_dn` - A DN of an object higher in the tree than all relevant group
   objects.
* `group_name_attribute` - The attribute used to identify the group (usually `cn`).
* `group_object_class_filter`- The filter which matches the objectClass(es) of group objects.
* _variant 1_
** `group_membership_attribute` - User object attribute containing group
  membership information. e.g. `memberOf`.
* _variant 2_
** `group_membership_attribute` - User object attribute containing group
 membership information. e.g. `memberOf`.
* _variant 3_
** `group_membership_dn_attribute` - An attribute in the group object referencing user objects by DN
e.g. `member`.
* _variant 4_
** `group_membership_uid_attribute` - An attribute in the group object referencing user objects by UID
e.g. `memberUID`.

.Locating groups in "mature" directories
****
As with users, groups may be located in multiple areas of the directory.

.Determining which objects contain groups
====
```
ldapsearch -x -H ldap://localhost:389 -b "dc=example,dc=com" -E 'pr=100' "(ObjectClass=posixGroup)" dn | grep dn: | sed -e 's/dn: [^,]*,//' | sort | uniq -c
3 ou=groups,dc=example,dc=com
1 ou=groups,o=long-defunct-org-that-was-merged-with-example-com,dc=example,dc=com
3 ou=groups,ou=interns-summer-project,dc=example,dc=com
2 ou=groups,ou=backup-restored-in-the-wrong-place,dc=example,dc=com
```
====
****

=== If the variant can't be determined

- There's a typographical error in the search filter or `ldapsearch` arguments.
- The bound user doesn't have sufficient permission to view user or group
objects. Contact your LDAP administrator.
- The LDAP directory uses a special scheme to represent groups which requires
querying a special OID, or providing additional search controls (outside of the
scope of this howto).
- Group memberships are represented with 'operational' attributes.  Repeat the
user object search with '+' set as the second positional argument.

.Searching for a user object and returning operational attributes
```
ldapsearch -z 10 -x -H ldap://localhost:389 -b "dc=example,dc=com" "(ObjectClass=posixAccount)" "*" "+"
```
