LdapLoginModule

LdapLoginModule

 

An implementation of LoginModule that authenticates against an LDAP (LDAPv3) server

using JNDI, based on the configuration properties. An alternate implementation is the LdapExtLoginModule.

 

The LoginModule options include whatever options your LDAP JNDI provider supports. Examples of standard property names are:

 

  • Context.INITIAL_CONTEXT_FACTORY = "java.naming.factory.initial"

  • Context.SECURITY_PROTOCOL = "java.naming.security.protocol"

  • Context.PROVIDER_URL = "java.naming.provider.url"

  • Context.SECURITY_AUTHENTICATION = "java.naming.security.authentication"

 

 

The Context.SECURITY_PRINCIPAL is set to the distinguished name of the user

as obtained by the callback handler and the Context.SECURITY_CREDENTIALS

property is either set to the String password or Object credential depending

on the useObjectCredential option.

 

 

The actual authentication happens in 2 phases:

  1. Binding to the directory: happens using the specified URL and the userDN constructed with the rules below (prefix + username (or what the user keyed in the prompt box) + suffix)

  2. Searching the directory for objects that match the specified rule. For example, uidAttributeID="sAMAccountName" for Active Directory will match objects for which sAMAccountName is what the user entered (and if the matchOnUserDN was true, it will try to match sAMAccountName = userDN as constructed above)

 

 

Additional module properties include

  • principalDNPrefix, principalDNSuffix : A prefix and suffix to add to the username when forming the user distiguished name. This is useful if you prompt a user for a username and you don't want them to have to enter the fully distinguished name. Using this property and principalDNSuffix the userDN will be formed as:

String userDN = principalDNPrefix + username + principalDNSuffix;
  • useObjectCredential : indicates that the credential should be obtained as an opaque Object using the org.jboss.security.plugins.ObjectCallback type of Callback rather than as a char{FOOTNOTE DEF  } password using a JAAS PasswordCallback.

  • rolesCtxDN : The fixed distinguished name to the context to search for user roles. Consider that this is not the Distinguished Name of where the actual roles are; rather, this is the DN of where the objects containing the user roles are (e.g. for active directory, this is the DN where the user account is)

  • userRolesCtxDNAttributeName: The name of an attribute in the user object that contains the distinguished name to the context to search for user roles. This differs from rolesCtxDN in that the context to search for a user's roles can be unique for each user.

  • uidAttributeID : Type of the attribute in the role object that identifies member user ids. This is used to locate the user's roles through an LDAP query. For example, the query might look like

    "(" + uidAttributeID + "={0})"

    . There are bugs related to this feature in JBoss 4.0.3SP1 and 4.0.3FINAL: http://jira.jboss.com/jira/browse/JBAS-2452

  • matchOnUserDN : A flag indicating if the search for user roles should match on the user's fully distinguished name. If false just the username is used as the match value. If true, the userDN is used as the match value.

  • allowEmptyPasswords : A flag indicating if empty(length==0) passwords should be passed to the ldap server. An empty password is treated as an anonymous login by some ldap servers and this may not be a desirable feature. Set this to false to reject empty passwords, true to have the ldap server validate the empty password. The default is true.

  • roleAttributeIsDN : A flag indicating whether the user's role attribute contains the fully distinguished name of a role object, or the users's role attribute contains the role name. If false, the role name is taken from the value of the user's role attribute. If true, the role attribute represents the distinguished name of a role object.  The role name is taken from the value of the roleNameAttributeId` attribute of the corresponding object.  In certain directory schemas (e.g., Microsoft Active Directory), role (group)attributes in the user object are stored as DNs to role objects instead of as simple names, in which case, this property should be set to true. The default value of this property is false.

  • roleNameAttributeID : The name of the attribute of the role object which corresponds to the name of the role.  If the `roleAttributeIsDN` property is set to true, this property is used to find the role object's name attribute. If the `roleAttributeIsDN` property is set to false, this property is ignored.

  • searchTimeLimit (4.0.3+): The time limit in milliseconds for the search. A value of 0 means to wait indefinitely.

  • searchScope (4.0.3+): Sets the search scope to one of the strings. The default is SUBTREE_SCOPE.

    • OBJECT_SCOPE : only search the named roles context.

    • ONELEVEL_SCOPE : search directly under the named roles context.

    • SUBTREE_SCOPE :  If the roles context is not a DirContext, search only the object. If the roles context is a DirContext, search the subtree rooted at the named object, including the named object itself.

  • java.naming.security.principal (4.0.3+): This standard JNDI property if specified in the login configuration, it is used to rebind to the ldap server after user authentication for the role searches. This may be necessar if the user does not have permission to perform these queres. If specified, the java.naming.security.credentials provides the rebind credentials.

  • java.naming.security.credentials (4.0.3+): This standard JNDI property if specified in the login configuration, it is used to rebind to the ldap server after user authentication for the role searches along with the java.naming.security.principal value. This can be encrypted by specifying the jaasSecurityDomain.

  • jaasSecurityDomain (4.0.3+): The JMX ObjectName of the JaasSecurityDomain to use to decrypt the java.naming.security.principal. The encrypted form of the password is that returned by the JaasSecurityDomainencrypt64(byte{FOOTNOTE DEF  }) method. The org.jboss.security.plugins.PBEUtils can also be used to generate the encrypted form.

 

Sample schemas and configs

 

There is a bug in JBoss-4.0.3SP1 and 4.0.3FINAL that effects the uidAttributeID option and causes JBoss to assign all roles to any authenticated user. The bug breaks these examples.

 

http://jira.jboss.com/jira/browse/JBAS-2465

 

 

The source patch suggested in the bug report is simple and works. JBoss-4.0.4GA fixes the issue.

 

    <application-policy name = "testLdapExample1">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.LdapLoginModule"
                          flag="required">
                <module-option name="java.naming.factory.initial"> 
                    com.sun.jndi.ldap.LdapCtxFactory
                    </module-option>
                <module-option name="java.naming.provider.url">
                    ldap://ldapserver/
                </module-option>
                <module-option name="java.naming.security.authentication">
                    simple
                </module-option>
                <module-option name="principalDNPrefix">uid=</module-option>                    
                <module-option name="principalDNSuffix">,ou=People,dc=jboss,dc=org</module-option>
                <module-option name="rolesCtxDN">ou=Roles,dc=jboss,dc=org</module-option>
                <module-option name="uidAttributeID">member</module-option>
                <module-option name="matchOnUserDN">true</module-option>
                <module-option name="roleAttributeID">cn</module-option>
                <module-option name="roleAttributeIsDN">false</module-option>
                <module-option name="searchTimeLimit">5000</module-option>
                <module-option name="searchScope">ONELEVEL_SCOPE</module-option>
            </login-module>
        </authentication>
    </application-policy>

 

 

    <application-policy name = "testLdapRebindExample1">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.LdapLoginModule"
                          flag="required">
                <module-option name="java.naming.factory.initial"> 
                    com.sun.jndi.ldap.LdapCtxFactory
                    </module-option>
                <module-option name="java.naming.provider.url">
                    ldap://ldapserver/
                </module-option>
                <module-option name="java.naming.security.authentication">
                    simple
                </module-option>
                <!-- Rebind as a user with search priviledges for the role queries -->
                <module-option name="java.naming.security.principal">cn=Root,dc=jboss,dc=org</module-option>                    
                <module-option name="java.naming.security.credentials">secret1</module-option>                    

                <module-option name="principalDNPrefix">uid=</module-option>                    
                <module-option name="principalDNSuffix">,ou=People,dc=jboss,dc=org</module-option>
                <module-option name="rolesCtxDN">ou=Roles,dc=jboss,dc=org</module-option>
                <module-option name="uidAttributeID">member</module-option>
                <module-option name="matchOnUserDN">true</module-option>
                <module-option name="roleAttributeID">cn</module-option>
                <module-option name="roleAttributeIsDN">false</module-option>
                <module-option name="searchTimeLimit">5000</module-option>
                <module-option name="searchScope">ONELEVEL_SCOPE</module-option>
            </login-module>
        </authentication>
    </application-policy>

 

    <application-policy name = "testLdapExample2">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.LdapLoginModule"
                          flag="required">
                <module-option name="java.naming.factory.initial"> 
                    com.sun.jndi.ldap.LdapCtxFactory
                    </module-option>
                <module-option name="java.naming.provider.url">
                    ldap://ldapserver/
                </module-option>
                <module-option name="java.naming.security.authentication">
                    simple
                </module-option>
                <module-option name="principalDNPrefix">uid=</module-option>                    
                <module-option name="principalDNSuffix">,ou=People,dc=jboss,dc=org</module-option>
                <module-option name="rolesCtxDN">ou=Roles,dc=jboss,dc=org</module-option>
                <module-option name="uidAttributeID">member</module-option>
                <module-option name="matchOnUserDN">false</module-option>
                <module-option name="roleAttributeID">memberOf</module-option>
                <module-option name="roleAttributeIsDN">true</module-option>
                <module-option name="roleNameAttributeID">cn</module-option>
            </login-module>
        </authentication>
    </application-policy>

 

A configuration for Active Directory in which ther user enters only the user only enters a simple username would be something like:

    <application-policy name="LdapToActiveDirectory">
        <authentication>
            <login-module code="org.jboss.security.auth.spi.LdapLoginModule" flag="required" >
                <module-option name="java.naming.provider.url">ldap://ldaphost.jboss.org:389/</module-option>
                <module-option name="rolesCtxDN">CN=Users,DC=ldaphost,DC=jboss,DC=org</module-option>
                <module-option name="matchOnUserDN">false</module-option>
                <module-option name="principalDNPrefix">CN=</module-option>
                <module-option name="principalDNSuffix">,CN=Users,DC=ldaphost,DC=jboss,DC=org</module-option>
                <module-option name="uidAttributeID">sAMAccountName</module-option>
                <module-option name="roleAttributeID">memberOf</module-option>
                <module-option name="roleAttributeIsDN">true</module-option>
                <module-option name="roleNameAttributeID">name</module-option>
            </login-module>
        </authentication>
    </application-policy>

 

Using a SSL secured LDAP connection

 

For using SSL you need to add the following option:

<module-option name="java.naming.security.protocol">ssl</module-option>

 

If the certificate the LDAP-Server uses is not signed by a trusted CA then you'll need to either import that certificate into your cacerts file or create you own truststore:

keytool -import -file ldapcert.der -keystore ldap.truststore

 

Now you need to change the settings to use the alternative truststore. That can be done in the properties-service.xml:

 

    <attribute name="Properties">
          javax.net.ssl.trustStore=../server/default/conf/ldap.truststore
          javax.net.ssl.trustStorePassword=somepw
    </attribute>

(given that ldap.truststore is located in server/default/conf)

 

If you get an error like this

Caused by: java.security.InvalidAlgorithmParameterException: the trustAnchors parameter must be non-empty
        at java.security.cert.PKIXParameters.setTrustAnchors(PKIXParameters.java:183)
...

you may have not set the path or the file name for the truststore correctly! The truststore seems not to be searched on the classpath, instead the path must be included correctly.

 

LDAP Failover

 

Multiple ldap provider urls can be passed via the module options as demonstrated in this tutorial.

http://java.sun.com/products/jndi/tutorial/ldap/misc/url.html#MULTI

 

 

There seems to be a related bug that has been fixed in JDK6.

 

 

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6394020

 

 

LDAP Referral

You can read up on it here http://java.sun.com/products/jndi/tutorial/ldap/referral/jndi.html

<module-option name="java.naming.referral">follow</module-option>

 

Referenced by: