SASL LDAP Proxy Authentication in OpenLDAP and Java


SASL is a way for handling authentication and “act-as” issues in protocol. LDAP is using it in an alternative way to direct simple binds.

This article will show how to use a SASL LDAP proxy authentication by adding it to OpenLDAP and using it through Java Naming and Directory Interface.

First, set up a OpenLDAP directory with fully functional SASL settings. Build or use an OpenLDAP directory that is linked to libsasl2 : you can check that libsasl2 is used :

$ ldd /usr/sbin/slapd |grep sasl
libsasl2.so.2 => /usr/lib/libsasl2.so.2 (0x00007fac2e546000)

If you want to build your own version of OpenLDAP, the required option is –with-cyrus-sasl.

Then suppose a directory with the following entries :

dc=foo,dc=bar
  |-cn=proxy
  |-ou=People
    |-uid=user1

We suppose you want to use the proxy account to act-as users (i.e. user1 in our sample) to search, read and write to the directory without using their passwords.

You need to understand the main SASL concepts :

  • the authentication id : this is the account used to connect to the directory, i.e. cn=proxy
  • the authorization id : the account authorized (or not) to act, i.e. uid=user1
  • the authentication credentials : a user password or any way to authenticate

In OpenLDAP, SASL is handle natively (by OpenLDAP itself) without the need of third-party configuration (no need of /usr/lib/sasl2/slapd.conf, this was the old way šŸ™‚ !). So let’s explain how it will handle SASL bind requests :

  • the initial messages are exchanged between the client and the server to check for the authentication method (PLAIN, DIGEST-MD5, …) and will provide server general information (realm, server name, supported ciphers, level for quality of protection, charset, …) and session shared secret
  • on the server side, the second step will use the provided authentication id by passing it to the ‘authz-regexp’ regular expression to get a authentication DN. This will be in the form uid=<authentication_id>,cn=<auth_method>,cn=auth where authentication_id will be replaced by the authentication id you have provided (username or full dn) and auth_method will be the SASL authentication method used (DIGEST-MD5, EXTERNAL, …). So if you plan to use the proxy username, you will require a regex such as
    authz-regexp
        "uid=([^,]*),cn=digest-md5,cn=auth"
        "ldap:///dc=foo,dc=bar??one?cn=$1"

    Then the authentication DN is used to check the password, which must be stored in a clear text format inside the userPassword attribute (take care that an “auth” access right is required). Therefore, you must ensure that password changes for the proxy account must always be stored in a clear text format (see password-hash OpenLDAP directive) and that the userPassword attribute is fully protected from an unauthorized retrieve
  • the second step is also a pattern matching from the authorization id against the same authz-regexp rules. Then you need the corresponding regexp :
    authz-regexp
        "uid=([^,]*),ou=People,dc=foo,dc=bar"
        "ldap:///ou=People,dc=foo,dc=bar??one?uid=$1"
  • the last step is to check if the authentication id is authorized to “act as” the authorization id. The way OpenLDAP check for that is against the authzTo and/or authzFrom attributes. These attributes define if the authentication id can be “authorized to” act as the authorization id and/or if the authorization id can be “authorized from” to act as the authentication id. An “auth” access right is also required on these attributes. Thus you need the following attribute inside the cn=proxy entry :
    authzTo: dn.regex: uid=[^,]+,ou=People,dc=foo,dc=bar

If all previous items are correct, you would be able to achieve successfully the following commands :

  • a sasl bind through a LDAP who am i extended operation :
    ldapwhoami -Y DIGEST-MD5 -U proxy
  • a sasl proxied bind :
    ldapwhoami -Y DIGEST-MD5 -U proxy -X "dn:uid=user1,ou=People,dc=foo,dc=bar"

You are now ready to get ride of the Java part.

First of all, check that the following requirements are fully satisfied :

  • a Sun JRE with Java Cryptographic Extension (JCE) deployed

Then create a new class, for example, SaslClientTest :

public class SaslClientTest {
}

Add a constructor and force Sun SASL Security Provider load :

  public SaslClientTest() {
    Security.addProvider(new com.sun.security.sasl.Provider());
  }

Inside the test method, create a property map and put required settings (that you may need to adapt to fit hostname, port, …)

  public void testSaslBind {
    Properties env = new Properties();
    env.put(InitialLdapContext.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    env.put(InitialLdapContext.PROVIDER_URL, "ldap://localhost:389/dc=foo,dc=bar");
    env.put(InitialLdapContext.LDAP_VERSION, "3");
    env.put(InitialLdapContext.SECURITY_AUTHENTICATION, "DIGEST-MD5");
    env.put(InitialLdapContext.SECURITY_PRINCIPAL, "proxy");
    env.put(InitialLdapContext.SECURITY_CREDENTIALS, "proxy");
  }

Now put the proxied authorization id :

    env.put("java.naming.security.sasl.authorizationId","dn:uid=user1,ou=People,dc=foo,dc=bar");

Then you only need to create your LDAP context with the standard method :

    LdapContext lc = new InitialLdapContext(env, null);

That’s it : your connection will be established with the proxy account but will be recognized and authorized as user1 directory entry. With a OpenLDAP 2.4, you may have the following type of log :

conn=4 fd=17 ACCEPT from IP=[::1]:34084 (IP=[::]:389)
conn=4 op=0 BIND dn=”” method=163
conn=4 op=0 RESULT tag=97 err=14 text=SASL(0): successful result:
conn=4 op=1 BIND dn=”” method=163
conn=4 op=1 BIND authcid=”proxy” authzid=”dn:uid=user1,ou=people,dc=foo,dc=bar”
conn=4 op=1 BIND dn=”cn=proxy,dc=foo,dc=bar” mech=DIGEST-MD5 sasl_ssf=128 ssf=128
conn=4 op=1 RESULT tag=97 err=0 text=
conn=4 op=2 SRCH base=”uid=user1,ou=people,dc=foo,dc=bar” scope=2 deref=0 filter=”(objectClass=*)”
conn=4 op=2 SRCH attr=*
conn=4 op=2 SEARCH RESULT tag=101 err=0 nentries=1 text=
conn=4 op=3 UNBIND
conn=4 fd=17 closed

Troubleshooting

  1. you can encountered errors in the first step (arround OpenLDAP). If you have follow instructions, launch OpenLDAP with a great loglevel (launch slapd with -d -1) and take a look at ACL messages : if at any moment, an attribute is not well authorized, your SASL bind will fail
  2. If you encountered an error in the second step, try to look at the messages exchanged with the server through the following complementary setting :
    env.put("com.sun.jndi.ldap.trace.ber", System.err);
Advertisements
This entry was posted in Identity, OpenSource. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s