Then, the application needs to compare the password that the user entered with the userPassword attribute. But this is what I don't understand - how does the application know the random salt that was generated when the user was first registered to the directory?
The salt is always stored right next to the hash. Depending on format, it might be visually separated (e.g. /etc/shadow "crypt" uses $
to separate the salt from the hash) or it might be combined (e.g. LDAP {SSHA}
will have X bytes of salt + Y bytes of hash combined, inside of the Base64 encoding).
The userPassword field is write-only, I can't retrieve it
This is because usually it's not the application that hashes and compares the password, but the LDAP server itself that does so, and the LDAP 'Compare' operation is not used for this purpose.
Instead, the application would use the LDAP 'Bind' operation and literally attempt to "log in" as the user to the LDAP server. That way, the application does not need to have 'read' access to the salt/hash values and doesn't even need to actually support the hash algorithm that the LDAP server uses.
(In fact, the LDAP server might not even store hashes at all – e.g. OpenLDAP may use {SASL}
to offload password verification to a completely separate system such as Kerberos. All of this is completely invisible to programs that use LDAP 'Bind' to verify the password.)
To emulate this through CLI tools, you would:
Use ldapsearch
with the webapp's dedicated credentials to search the directory for the user's DN (as the user provides only the username).
Use ldapsearch -D <user_dn> -w <user_passwd>
to authenticate, ignoring the search result (as there is no CLI tool that only binds).
Most programs do not use the CLI; they use the LDAP client library directly, so they can call ldap_simple_bind() independently and immediately unbind after knowing the result.
However, some webapps do make an actual LDAP search at this step, reading the user's own DN entry with their own privileges – e.g. to retrieve certain fields that the webapp's own account isn't allowed to read.
If you don't care about the result, looking up the rootDSE -b "" -s base
is as close to a "no-op" search as it gets. You could also use ldapwhoami
instead of ldapsearch
(but not all LDAP servers support the 'WhoAmI' exop, e.g. AD doesn't).