10

I'm setting up OpenLDAP slapd on Ubuntu 14.04 Trusty Tahr. I want certain instances (replication etc.) that aren't users to be able to login via SASL using DIGEST-MD5 mechanism.

Unlike users, they are not supposed to have a corresponding DN (along with the password) in the directory tree. Instead, their credentials are supposed to be stored externally, hence SASL.

I'm using saslauthd right now (this is not a hard requirement if it can be made to work with direct access to the sasldb, for instance) and it works fine using mechanisms PLAIN and LOGIN while it fails using mechanisms DIGEST-MD5 and CRAM-MD5.

What am I missing or doing wrong? How can I get it to work with DIGEST-MD5?


OpenLDAP is configured for SASL in /etc/ldap/sasl2/slapd.conf like this:

mech_list: EXTERNAL DIGEST-MD5 CRAM-MD5 PLAIN LOGIN
pwcheck_method: saslauthd
saslauthd_path: /var/run/saslauthd/mux


The interesting (changed) options in /etc/default/saslauthd are:

START=yes
MECHANISMS="sasldb"

They result in saslauthd being started like so:

/usr/sbin/saslauthd -a sasldb -c -m /var/run/saslauthd -n 5


I reproduce the failing case with DIGEST-MD5 like this:

# ldapsearch -U replication -ZZ -Y DIGEST-MD5 -H ldap://ldap-master.example.com/ -b "dc=example,dc=com" "(objectClass=*)"
SASL/DIGEST-MD5 authentication started
Please enter your password: 
ldap_sasl_interactive_bind_s: Invalid credentials (49)
    additional info: SASL(-13): user not found: no secret in database

The part in the slapd log where it seems to fail (logging is on any) looks like this:

slapd[23330]: [rw] authid: "uid=replication,cn=digest-md5,cn=auth" -> "uid=replication,cn=digest-md5,cn=auth"
slapd[23330]: slap_parseURI: parsing uid=replication,cn=digest-md5,cn=auth
slapd[23330]: >>> dnNormalize: <uid=replication,cn=digest-md5,cn=auth>
slapd[23330]: <<< dnNormalize: <uid=replication,cn=digest-md5,cn=auth>
slapd[23330]: <==slap_sasl2dn: Converted SASL name to uid=replication,cn=digest-md5,cn=auth
slapd[23330]: slap_sasl_getdn: dn:id converted to uid=replication,cn=digest-md5,cn=auth
slapd[23330]: SASL Canonicalize [conn=1002]: slapAuthcDN="uid=replication,cn=digest-md5,cn=auth"
slapd[23330]: SASL [conn=1002] Failure: no secret in database
slapd[23330]: SASL [conn=1002] Debug: DIGEST-MD5 common mech dispose
slapd[23330]: send_ldap_result: conn=1002 op=2 p=3
slapd[23330]: send_ldap_result: err=49 matched="" text="SASL(-13): user not found: no secret in database"
slapd[23330]: send_ldap_response: msgid=3 tag=97 err=49

There's nothing in /var/log/auth.log nor in the debug output of saslauthd when I run it manually, which likely indicates that slapd didn't even get as far as handing off the authentication to saslauthd (in contrast with the working case, see below).


I reproduce the working case with PLAIN or LOGIN like this:

# ldapsearch -U replication -ZZ -Y PLAIN -H ldap://ldap-master.example.com/ -b "dc=example,dc=com" "(objectClass=*)"
SASL/PLAIN authentication started
Please enter your password: 
SASL username: replication
SASL SSF: 0
# extended LDIF
…

The corresponding part in the slapd log that indicated failure for the case above now looks like this:

slapd[23330]: [rw] authid: "uid=replication,cn=plain,cn=auth" -> "uid=replication,cn=plain,cn=auth"
slapd[23330]: slap_parseURI: parsing uid=replication,cn=plain,cn=auth
slapd[23330]: >>> dnNormalize: <uid=replication,cn=plain,cn=auth>
slapd[23330]: <<< dnNormalize: <uid=replication,cn=plain,cn=auth>
slapd[23330]: <==slap_sasl2dn: Converted SASL name to uid=replication,cn=plain,cn=auth
slapd[23330]: slap_sasl_getdn: dn:id converted to uid=replication,cn=plain,cn=auth
slapd[23330]: SASL Canonicalize [conn=1006]: slapAuthcDN="uid=replication,cn=plain,cn=auth"
slapd[23330]: daemon: activity on 1 descriptor
slapd[23330]: daemon: activity on:
slapd[23330]: 
slapd[23330]: daemon: epoll: listen=8 active_threads=0 tvp=zero
slapd[23330]: daemon: epoll: listen=9 active_threads=0 tvp=zero
slapd[23330]: daemon: epoll: listen=10 active_threads=0 tvp=zero
slapd[23330]: SASL proxy authorize [conn=1006]: authcid="replication" authzid="replication"
slapd[23330]: conn=1006 op=1 BIND authcid="replication" authzid="replication"
slapd[23330]: SASL Authorize [conn=1006]:  proxy authorization allowed authzDN=""
slapd[23330]: send_ldap_sasl: err=0 len=-1
slapd[23330]: conn=1006 op=1 BIND dn="uid=replication,cn=plain,cn=auth" mech=PLAIN sasl_ssf=0 ssf=128
slapd[23330]: do_bind: SASL/PLAIN bind: dn="uid=replication,cn=plain,cn=auth" sasl_ssf=0
slapd[23330]: send_ldap_response: msgid=2 tag=97 err=0

Both /var/log/auth.log and the debug output from saslauthd now contain this:

saslauthd[23354]: rel_accept_lock : released accept lock
saslauthd[23358]: get_accept_lock : acquired accept lock
saslauthd[23354]: cache_get_rlock : attempting a read lock on slot: 458
saslauthd[23354]: cache_lookup    : [login=replication] [service=] [realm=ldap]: not found, update pending
saslauthd[23354]: cache_un_lock   : attempting to release lock on slot: 458
saslauthd[23354]: cache_get_wlock : attempting a write lock on slot: 458
saslauthd[23354]: cache_commit    : lookup committed
saslauthd[23354]: cache_un_lock   : attempting to release lock on slot: 458
saslauthd[23354]: do_auth         : auth success: [user=replication] [service=ldap] [realm=] [mech=sasldb]
saslauthd[23354]: do_request      : response: OK


Apparently, there must be some difference in how it works with PLAIN and LOGIN vs. DIGEST-MD5 and CRAM-MD5.


Update and clarification:

The DNs used for authorizing access to the tree are uid=replication,cn=digest-md5,cn=auth and uid=replication,cn=plain,cn=auth respectively and according to section 15.2.5 of http://www.openldap.org/doc/admin24/sasl.html those DNs don't need to actually exist in the tree (which must be true at least for PLAIN and LOGIN as it is working fine there).
For testing purposes I'm currently using olcAccess: to * by dn.regex="replication" read by * break to ensure the replication login gets at least read access to everything (yes, I know that's not secure and I will give it proper permissions for production) in the master LDAP tree.

The credentials are in /etc/sasldb2 and they are successfully checked against when using PLAIN or LOGIN (see above). For reference, it looks like that:

# sasldblistusers2
replication@ldap-master: userPassword
…

# db_dump -p /etc/sasldb2 
VERSION=3
format=print
type=hash
h_nelem=4
db_pagesize=4096
HEADER=END
 replication\00ldap-master\00userPassword
 PasswordCensored
…

However, in the case of DIGEST-MD5 and CRAM-MD5 it doesn't seem to contact saslauthd at all (due to me missing something or doing something wrong, possibly), so the database is likely not consulted, either.

blubberdiblub
  • 595
  • 1
  • 5
  • 15
  • How does ldap know what access controls the connection is allowed if it doesn't have an associated DN? You also didn't show if you added the shared secret to sasldb. – Andrew Domaszek Dec 19 '15 at 09:15
  • The access to the tree is a higher level problem that's not relevant here. It does get assigned a DN when doing an SASL login (`uid=replication,cn=digest-md5,cn=auth` and `uid=replication,cn=plain,cn=auth` as can be seen in the log outputs above) and according to the Note in section 15.2.5 of http://www.openldap.org/doc/admin24/sasl.html the DN doesn't need to exist in the tree. And the working `PLAIN` and `LOGIN` mechanisms suggest that this is true. Also, the working `PLAIN` and `LOGIN` cases show that the credentials are correctly taken from the sasldb. I will clarify that in my question. – blubberdiblub Dec 19 '15 at 11:28
  • Does DNS have reverse pointer records for the target servers? – Will Dec 21 '15 at 13:12
  • Yes, all our nodes have PTR records (and address -> domain -> address resolves to the same address, if it matters). – blubberdiblub Dec 21 '15 at 16:39

3 Answers3

5

My recipe is for OpenLDAP to check directly /etc/sasldb2.

First step: ensure the /etc/sasldb2 is owned by slapd user.

Next step: have slapd not to look for credentials in directory tree, which is done as following:

dn: cn=config
changetype: modify
replace: olcSaslAuxprops
olcSaslAuxprops: sasldb

Later, you will also need a olcAuthzRegexp rule, but in order to test if auth works, it is not necessary.

These settings are working on Debian GNU/Linux Jessie OpenLDAP-2.4.40 builded from source.

473183469
  • 1,350
  • 1
  • 12
  • 23
  • Originally I tried with and without `sasldb` in `olcSaslAuxprops` and it didn't make a difference for me. I did some extensive tests and it turns out that setting `olcSaslAuxprops` is one puzzle piece of a working configuration, so I'm accepting your solution. – blubberdiblub Jan 06 '16 at 15:39
2

The CRAM-MD5 and DIGEST-MD5 methods are impossible with "pwcheck_method: saslauthd". They needs plain, unencrypted passwords in a LDAP directory itself.

kupson
  • 3,388
  • 18
  • 18
  • Why? Alternatively, what about `pwcheck_method: auxprop` and having slapd thus access the sasldb directly? This should be possible, right? The passwords in the sasldb are plaintext. If possible, how do I configure that? – blubberdiblub Dec 20 '15 at 19:07
  • 1
    I can only point to http://www.openldap.org/lists/openldap-technical/201511/msg00142.html if you want to try "auxprop". I never tried it myself. – kupson Dec 20 '15 at 19:30
  • It turns out that setting `pwcheck_method` to one value or another doesn't make a difference at all for mechanisms `DIGEST-MD5` and `CRAM-MD5` (however, it does for `PLAIN` and `LOGIN`). Thus your answer doesn't apply to the problem. – blubberdiblub Jan 06 '16 at 15:42
0

I ran some extensive tests against various configurations with different credentials in the sasldb.

In conclusion it turns out the problem that most haunted me here was that, according to which authentication method (saslauthd vs. auxprop) is used (which in turn is dependent on the requested authentication mechanism - DIGEST-MD5/CRAM-MD5 vs. PLAIN/LOGIN), it was looking for a different domain. I did not expect that and as I had only one user-domain-pair in the sasldb, it didn't find the other one.

I'll list some of my conclusions and observations in hope that it will help others understand the problem and work around it:

  1. In case you request PLAIN or LOGIN mechanism, slapd will use the authentication method configured under pwcheck_method in /etc/ldap/sasl2/slapd.conf.
  2. In case you request DIGEST-MD5 or CRAM-MD5 mechanism, slapd will always use the auxprop method, regardless of what is configured for pwcheck_method.
  3. In effect, it means you can have slapd use the same method for all 4 mechanisms if you configure pwcheck_method: auxprop. Or you can decide to use 2 different methods if you configure something else for pwcheck_method.
  4. In any case, auxprop_plugin in /etc/ldap/sasl2/slapd.conf is ignored. Instead slapd uses what is configured for olcSaslAuxprops in its own configuration database. Note that this is true both for the implicit auxprop method when requesting DIGEST-MD5 or CRAM-MD5 mechanisms as well as for the explicitly configured pwcheck_method: auxprop when requesting one of the other 2 mechanisms.
  5. In my case, saslauthd looks up the credentials with the unadorned hostname (i. e. just ldap-master) as the domain component. If I were to take an educated guess, I'd say it uses something like gethostname() to come up with that, so this might be configurable somehow.
  6. In my case, when going through auxprop one way or the other, slapd uses the fully qualified domain name (i. e. ldap-master.example.com) as the domain component. It likely gets that from the original request URL (my alternatives for generating the request are limited when I want to use STARTTLS and want to be able to correctly verify the server certificate, which asks for a matching domain name), but it can likely be configured by setting olcSaslHost in slapd's configuration database. Note that I haven't tried configuring it.

So for coming up with a working configuration, you have several options:

  1. If you're fine with just PLAIN and LOGIN mechanisms over STARTTLS, configure pwcheck_method in /etc/ldap/sasl2/slapd.conf as required. If it's going to be auxprop, also configure olcSaslAuxprops in slapd's configuration database (set it to sasldb, for instance, if you want to use /etc/sasldb2 as credential store).
  2. If you're fine with just DIGEST-MD5 (I usually see CRAM-MD5 recommended against due to inferior security, so try avoiding it), set olcSaslAuxprops in slapd's configuration database, as the auxprop method is going to be used, no matter what you configure for pwcheck_method.
  3. If you want to be able to use mechanisms from both the set DIGEST-MD5, CRAM-MD5 (however, try to avoid CRAM-MD5) as well as the set PLAIN, LOGIN (make sure those are only used over an encrypted connection, like a STARTTLS-one) and prefer to use the same authentication method for all of them as well as check against the same set of credentials, you're limited to auxprop. Configure pwcheck_method: auxprop in /etc/ldap/sasl2/slapd.conf and set olcSaslAuxprops in slapd's configuration database as required.
  4. If you want to be able to use mechanisms from both sets while using different authentication methods for them (sounds like an administration nightmare to me, but there you go), configure pwcheck_method in /etc/ldap/sasl2/slapd.conf accordingly for PLAIN and LOGIN and remember that you're forced to use auxprop for DIGEST-MD5 (and CRAM-MD5 if you insist) and set olcSaslAuxprops in slapd's configuration database as required.

    • If you happen to configure saslauthd for unhashed mechanisms and make sure that saslauthd accesses a different database than what slapd accesses through auxprop or use something else than sasldb for olcSaslAuxprops, you have them nicely separated and nothing to worry about.

    • If you happen to configure saslauthd for unhashed mechanisms and sasldb for auxprop for the hashed mechanisms and have them access the same credentials database, you have 3 options, in order of preference:

      1. Somehow, make sure that both saslauthd as well as slapd accessing the sasldb directly through auxprop query the credentials with the same domain (try setting olcSaslHost or coercing saslauthd to use a FQDN), so you will only have to care about one credential entry per entity to authenticate.
      2. Deliberately make the decision to use different credentials for the unhashed vs. the hashed mechanisms. Use a userid-hostname pair for the unhashed mechanisms over saslauthd and use a userid-hostname.domain.name pair for the hashed mechanisms via auxprop.
      3. Keep two credential entries per entity to authenticate - one with 'hostname' and the other with 'hostname.domain.name' - and keep the passwords of both in sync (ugh).
blubberdiblub
  • 595
  • 1
  • 5
  • 15