new blog 2.0


0x06. [LPIC-301] LDAP - Tightening security

In the previous post I confugured nss_ldap and pam_ldap, but the ACL applied to this configuration not only allows users to read other users passwords but also change them! We should change it ASAP.
The OpenLDAP ACL syntax is not too complicated and it lets us express all we need. The general syntax rule is:
access to (resource)
by (whom) (access level)
resource can be represented as
*any entry in the entire directory
regular expressiona regex denoting an entry or a group of entries. There are four methods of inheriting the expression:
- dn.base=regex
- dn.children=regex
- dn.subtree=regex
filter=(LDAP-Filter)A standard LDAP filter, e.g. ...
attrs=A list of attribures with specific access control. A very common example would be userPassword which should not be read by anyone but the owner who also should have write permission in order to change it.
who can be represented as:
anyonenon authenticated usersauthenticated usersuser trying to get access to his DN entryuser defined by the specific DNan ldap group in DN notation
and finally access level is one of the following:
none-no access is granted
authxThe right for authentication. Client sends credentials to the server, server compares and responds.
comparecIf the client knows what's the value of the entry already, the value can be confirmed already. Works similar to auth access level.
searchsability to search the entry with filters
readrfull read access
writewfull write access

Note: There are more options for OpenLDAP ACL syntax. For complete reference go to OpenLDAP Admin Guide

Based on that we want to come up with a solution which would:
- not let anybody change other user password
- not let anybody read other user password
- still be able to read other users contact data
Here is the answer:
access to attrs=userPassword
by self write
by * auth
access to *
by * read
The sequence in which the ACLs are defined is of great importance. Basically, you always should specify the most specific entries first, followed by less specific ones, finishing with totally general definitions, like in the example above. That's the reason behind it: the first matching entry from the top gets applied.

TCP Wrappers
slapd is a tcpd aware server. You can configure /etc/hosts.deny and /etc/hosts.allow accordingly to increase security. It's not a good idea though to trust TCP wrappers completely. iptables should be the preffered method of securing the network environment. Anyway, this configuration should cut off connections from the outside networks:

# /etc/hosts.allow
slapd: : ALLOW

# /etc/hosts.deny
slapd: ALL : DENY

Security Strength Factor is not the best documented feature. According to the OpenLDAP Admin Guide SSF indicates the relative strength of protection, whereby 0 = no protection at all, 1 = data integrity check, 56 = DES encryption, 112 = 3DES and everything over 128 denotes one of the strong, modern algorithms like AES, RC4 or Blowfish. Different SSF value depends on the key length.

SSF will be used during ACL planning and SASL authentication.

An alternative to simple binds is Simple Authentication and Security Layer. It is a framework of different standarized security mechanisms providing strong authentication and data integrity checks. I will discuss implementation of SASL DIGEST-MD5 and GSSAPI configuration.

Before you start troubleshooting your SASL configuration, it's worth checking if your slapd supports SASL at all. This command is very helpful in determining this:

$ ldapsearch -x -s "base" -b "" supportedSASLMechanisms

# extended LDIF
# LDAPv3
# base <> with scope baseObject
# filter: (objectclass=*)
# requesting: supportedSASLMechanisms

supportedSASLMechanisms: GSSAPI
supportedSASLMechanisms: NTLM
supportedSASLMechanisms: LOGIN
supportedSASLMechanisms: PLAIN
supportedSASLMechanisms: DIGEST-MD5
supportedSASLMechanisms: CRAM-MD5

# search result
search: 2
result: 0 Success

# numResponses: 2
# numEntries: 1

OK, my server supports 6 types of SASL authentication. If your does not, the only solution now is to shut it down, (re)compile cyrus-sasl package with appropriate options (-> Kerberos!!!, otherwise no GSSAPI) and recompile the entire openldap again. If you don't do it you can see a message saying "ldap_sasl_interactive_bind_s: No such attribute" when running openldap tools on the client side. In this case the message means literally "your server does not have support for SASL".

The DIGEST-MD5 assumes that both the server and the client know the shared secret, that is a password. During the authentication process the following occurs:
1. Connection is initiated
2. Server sends a challenge to the client, it is based on a known password
3. Client responds to the server with a response to the challenge.
4. Server refers to the response in order to decide if the client knows the password.

As always, some minor corrections of slapd.conf are necessary. I had to enrich my slapd.conf with the following snippet:
sasl-realm PORTA.TUX
sasl-host porta.tux
The first two lines define the SASL realm and the hostname/IP address of the SASL host to refer. Following lines are a translation of "ldap dn username" to "sasl username" mapping.

According to the LDAP-HOWTO, the uid is taken from SASL and mapped to it's LDAP DN counterpart.
The first part of the configuration directove has the following format:
where "mechanism" is the SASL mechanism applied - in my example - "digest-md5".

In DIGEST-MD5 passwords can be stored either in SASL database (accessible via saslpasswd2/sasldblistusers2 commands) or directly in the LDAP directory, in the latter case however the password hash must be set to {CLEARTEXT}

I chose to store my passwords in the directory itself. Now, let's see the outcome.

$ ldapwhoami -U spitfire -Y DIGEST-MD5

SASL/DIGEST-MD5 authentication started
Please enter your password:
SASL username: spitfire
SASL installing layers
Result: Success (0)

GSSAPI for Kerberos
SASL GSSAPI mechanism used in conjunction with Kerbeos V allows you to use SSO (Single Sign On). That is, you enter your password once only, when you request a kerberos ticket, and you can work on being fully authenticated. Next time you need to enter your password would be either when the ticket expires or if you destroy it. Practically, it creates less potential opportunities for the attackers to take over your password (e.g. by keylogging), but the users must be educated to lock their screens / logout when they leave the workplace for even short time.

1. Let's start with changing the authz-regexp directive in slapd to the following format:
2. You need to create a kerberos principal ldap/your.realm. Use addprinc, ktadd, cpw within kadmin.local to create it and export the kerberos keytab. Create another principal for your user (spitfire).
3. Make sure your slapd can read keytab while running. Otherwise you won't be able to authenticate.
# chown root:ldap /etc/krb5.keytab
# chmod 660 /etc/krb5.keytab
# /etc/init.d/slapd restart
4. Let's try to authenticate with GSSAPI.
# ldapsearch
SASL/GSSAPI authentication started
ldap_sasl_interactive_bind_s: Local error (-2)
additional info: SASL(-1): generic failure: GSSAPI Error: Unspecified GSS failure. Minor code may provide more information (No credentials cache found)
# kinit spitfire/porta.tux
Password for spitfire/porta.tux@PORTA.TUX:
# ldapwhoami
SASL/GSSAPI authentication started
SASL username: spitfire/porta.tux@PORTA.TUX
SASL installing layers
Result: Success (0)
# klist
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: spitfire/porta.tux@PORTA.TUX

Valid starting Expires Service principal
12/23/07 03:53:38 12/24/07 03:53:38 krbtgt/PORTA.TUX@PORTA.TUX
12/23/07 03:53:50 12/24/07 03:53:38 ldap/porta.tux@PORTA.TUX

Works OK. Note: I didn't have to enter my password while performing ldapwhoami.

No comments: