LDAP+TLS feature request

Hi everyone

It would be nice if Wildfire could connect to the LDAP-Server using a TLS connection.
This would make it possible to use client-certificates and to check the provided server-certificate using the keystore.
I already started with a patch, but I couldn’‘t test it yet and I’'m not a java programmer…

Greets,
Tiziano


diff -Naurb src/java/org/jivesoftware/wildfire/ldap.orig/LdapGroupProvider.java src/java/org/jivesoftware/wildfire/ldap/LdapGroupProvider.java
— src/java/org/jivesoftware/wildfire/ldap.orig/LdapGroupProvider.java 2006-11-26 18:07:55.000000000 +0100
+++ src/java/org/jivesoftware/wildfire/ldap/LdapGroupProvider.java 2006-11-26 18:05:35.000000000 +0100
@@ -32,6 +32,7 @@
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import java.io.IOException;

/**

  • LDAP implementation of the GroupProvider interface. All data in the directory is treated as
    @@ -597,7 +598,7 @@

  • @return a collection of groups.

  • @throws javax.naming.NamingException
    */

  • private Collection answer) throws NamingException, IOException {
    if (manager.isDebugEnabled()) {
    Log.debug(“Starting to populate groups with users.”);
    }
    diff -Naurb src/java/org/jivesoftware/wildfire/ldap.orig/LdapManager.java src/java/org/jivesoftware/wildfire/ldap/LdapManager.java
    — src/java/org/jivesoftware/wildfire/ldap.orig/LdapManager.java 2006-11-26 18:07:55.000000000 +0100
    +++ src/java/org/jivesoftware/wildfire/ldap/LdapManager.java 2006-11-26 18:03:57.000000000 +0100
    @@ -24,10 +24,13 @@
    import javax.naming.directory.SearchResult;
    import javax.naming.ldap.InitialLdapContext;
    import javax.naming.ldap.LdapContext;
    +import javax.naming.ldap.StartTlsResponse;
    +import javax.naming.ldap.StartTlsRequest;
    import java.net.URLEncoder;
    import java.util.*;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    +import java.io.IOException;

/**

  • Centralized administration of LDAP connections. The {@link #getInstance()} method
    @@ -53,6 +56,7 @@

  •  <li>ldap.groupSearchFilter</li>
    
  •  <li>ldap.debugEnabled</li>
    
  •  <li>ldap.sslEnabled</li>
    
  •  <li>ldap.autoFollowReferrals</li>
    
  •  <li>ldap.initialContextFactory --  if this value is not specified,
    
  •      "com.sun.jndi.ldap.LdapCtxFactory" will be used.</li>
    

@@ -137,6 +141,7 @@
private String adminPassword;
private boolean ldapDebugEnabled = false;
private boolean sslEnabled = false;

  • private boolean tlsEnabled = false;
    private String initialContextFactory;
    private boolean followReferrals = false;
    private boolean connectionPoolEnabled = true;
    @@ -256,6 +261,11 @@
    if (sslEnabledStr != null) {
    sslEnabled = Boolean.valueOf(sslEnabledStr);
    }
  •    tlsEnabled = false;
    
  •    String tlsEnabledStr = properties.get("ldap.tlsEnabled");
    
  •    if (tlsEnabledStr != null) {
    
  •        tlsEnabled = Boolean.valueOf(tlsEnabledStr);
    
  •    }
       followReferrals = false;
       String followReferralsStr = properties.get("ldap.autoFollowReferrals");
       if (followReferralsStr != null) {
    

@@ -299,6 +309,7 @@
buf.append("\t subTreeSearch:").append(subTreeSearch).append("\n");
buf.append("\t ldapDebugEnabled: “).append(ldapDebugEnabled).append(”\n");
buf.append("\t sslEnabled: “).append(sslEnabled).append(”\n");

  •    buf.append("\t tlsEnabled: ").append(tlsEnabled).append("\n");
       buf.append("\t initialContextFactory: ").append(initialContextFactory).append("\n");
       buf.append("\t connectionPoolEnabled: ").append(connectionPoolEnabled).append("\n");
       buf.append("\t autoFollowReferrals: ").append(followReferrals).append("\n");
    

@@ -325,7 +336,7 @@

  • @return a connection to the LDAP server.

  • @throws NamingException if there is an error making the LDAP connection.
    */

  • public LdapContext getContext() throws NamingException {

  • public LdapContext getContext() throws NamingException, IOException {
    try {
    return getContext(baseDN);
    }
    @@ -347,7 +358,7 @@
  • @return a connection to the LDAP server.

  • @throws NamingException if there is an error making the LDAP connection.
    */

  • public LdapContext getContext(String baseDN) throws NamingException {

  • public LdapContext getContext(String baseDN) throws NamingException, IOException {
    boolean debug = Log.isDebugEnabled();
    if (debug) {
    Log.debug(“Creating a DirContext in LdapManager.getContext()…”);
    @@ -364,7 +375,7 @@
    }

    // Use simple authentication to connect as the admin.
    
  •    if (adminDN != null) {
    
  •    if (adminDN != null && !tlsEnabled) {
           env.put(Context.SECURITY_AUTHENTICATION, "simple");
           env.put(Context.SECURITY_PRINCIPAL, adminDN);
           if (adminPassword != null) {
    

@@ -389,11 +400,27 @@
if (debug) {
Log.debug(“Created hashtable with context values, attempting to create context…”);
}
+
// Create new initial context
LdapContext context = new InitialLdapContext(env, null);
if (debug) {

  •        Log.debug("... context created successfully, returning.");
    
  •        Log.debug("... context created successfully.");
    
  •    }
    
  •    if (tlsEnabled) {
    
  •        StartTlsResponse tls = (StartTlsResponse) context.extendedOperation(new StartTlsRequest());
    
  •        tls.negotiate();
    
  •        Log.debug("... TLS successfully initialized.");
    
  •        if (adminDN != null) {
    
  •            context.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
    
  •            context.addToEnvironment(Context.SECURITY_PRINCIPAL, adminDN);
    
  •            if (adminPassword != null) {
    
  •                context.addToEnvironment(Context.SECURITY_CREDENTIALS, adminPassword);
    
  •            }
    
  •            context.reconnect(context.getConnectControls());
       }
    
  •        Log.debug("... and reconnected successfully.");
    
  •    }
    
  •    return context;
    
    }

@@ -405,13 +432,13 @@

  • @param password the user’'s password.

  • @return true if the user successfully authenticates.
    */

  • public boolean checkAuthentication(String userDN, String password) {

  • public boolean checkAuthentication(String userDN, String password) throws IOException {
    boolean debug = Log.isDebugEnabled();
    if (debug) {
    Log.debug("In LdapManager.checkAuthentication(userDN, password), userDN is: " + userDN + “…”);
    }
  •    DirContext ctx = null;
    
  •    LdapContext ctx = null;
       try {
           // See if the user authenticates.
           Hashtable<String, Object> env = new Hashtable<String, Object>();
    

@@ -422,12 +449,14 @@
“org.jivesoftware.util.SimpleSSLSocketFactory”);
env.put(Context.SECURITY_PROTOCOL, “ssl”);
}

  •        if (!tlsEnabled) {
           env.put(Context.SECURITY_AUTHENTICATION, "simple");
           env.put(Context.SECURITY_PRINCIPAL, userDN + "," + baseDN);
           env.put(Context.SECURITY_CREDENTIALS, password);
    
  •        }
           // Specify timeout to be 10 seconds, only on non SSL since SSL connections
           // break with a timemout.
    
  •        if (!sslEnabled) {
    
  •        if (!sslEnabled && !tlsEnabled) {
               env.put("com.sun.jndi.ldap.connect.timeout", "10000");
           }
           if (ldapDebugEnabled) {
    

@@ -440,9 +469,19 @@
if (debug) {
Log.debug(“Created context values, attempting to create context…”);
}

  •        ctx = new InitialDirContext(env);
    
  •        ctx = new InitialLdapContext(env, null);
           if (debug) {
    
  •            Log.debug("... context created successfully, returning.");
    
  •            Log.debug("... context created successfully.");
    
  •        }
    
  •        if (tlsEnabled) {
    
  •            StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
    
  •            tls.negotiate();
    
  •            Log.debug("... TLS successfully initialized.");
    
  •            ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
    
  •            ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN + "," + baseDN);
    
  •            ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
    
  •            ctx.reconnect(ctx.getConnectControls());
    
  •            Log.debug("... and reconnected successfully.");
           }
       }
       catch (NamingException ne) {
    

@@ -466,12 +505,14 @@
env.put(“java.naming.ldap.factory.socket”, “org.jivesoftware.util.SimpleSSLSocketFactory”);
env.put(Context.SECURITY_PROTOCOL, “ssl”);
}

  •                if (!tlsEnabled) {
                   env.put(Context.SECURITY_AUTHENTICATION, "simple");
                   env.put(Context.SECURITY_PRINCIPAL, userDN + "," + alternateBaseDN);
                   env.put(Context.SECURITY_CREDENTIALS, password);
    
  •                }
                   // Specify timeout to be 10 seconds, only on non SSL since SSL connections
                   // break with a timemout.
    
  •                if (!sslEnabled) {
    
  •                if (!sslEnabled && !tlsEnabled) {
                       env.put("com.sun.jndi.ldap.connect.timeout", "10000");
                   }
                   if (ldapDebugEnabled) {
    

@@ -483,7 +524,19 @@
if (debug) {
Log.debug(“Created context values, attempting to create context…”);
}

  •                ctx = new InitialDirContext(env);
    
  •                ctx = new InitialLdapContext(env, null);
    
  •                Log.debug("... context created successfully.");
    
  •                if (tlsEnabled) {
    
  •                    StartTlsResponse tls = (StartTlsResponse) ctx.extendedOperation(new StartTlsRequest());
    
  •                    tls.negotiate();
    
  •                     Log.debug("... TLS successfully initialized.");
    
  •                     ctx.addToEnvironment(Context.SECURITY_AUTHENTICATION, "simple");
    
  •                     ctx.addToEnvironment(Context.SECURITY_PRINCIPAL, userDN + "," + baseDN);
    
  •                     ctx.addToEnvironment(Context.SECURITY_CREDENTIALS, password);
    
  •                     ctx.reconnect(ctx.getConnectControls());
    
  •                     Log.debug("... and reconnected successfully.");
    
  •                }
               }
               catch (NamingException e) {
                   if (debug) {
    

@@ -575,7 +628,7 @@

  • @throws Exception if the search for the dn fails.

  • @see #findUserDN(String) to search using the default baseDN and alternateBaseDN.
    */

  • public String findUserDN(String username, String baseDN) throws Exception {

  • public String findUserDN(String username, String baseDN) throws Exception, IOException {
    boolean debug = Log.isDebugEnabled();
    if (debug) {
    Log.debug("Trying to find a user’'s DN based on their username. " + usernameField + ": " + username
    @@ -778,6 +831,16 @@
    }

    /**

  • * Returns true if LDAP connection is via TLS or not. TLS is turned off by default.
    
  • *
    
  • * @return true if TLS connections are enabled or not.
    
  • */
    
  • public boolean isTlsEnabled() {

  •    return tlsEnabled;
    
  • }

  • /**

  • Sets whether the connection to the LDAP server should be made via ssl or not.
    *

  • @param sslEnabled true if ssl should be enabled, false otherwise.
    @@ -788,6 +851,17 @@
    }

    /**

  • * Sets whether the connection to the LDAP server should be made via tls or not.
    
  • *
    
  • * @param tlsEnabled true if tls should be enabled, false otherwise.
    
  • */
    
  • public void setTlsEnabled(boolean tlsEnabled) {
  •    this.tlsEnabled = tlsEnabled;
    
  •    properties.put("ldap.tlsEnabled", Boolean.toString(tlsEnabled));
    
  • }
  • /**
  • Returns the LDAP field name that the username lookup will be performed

  • on. By default this is “uid”.
    *