Posts Tagged ‘ Active Directory

Integrating LDAP Authentication with Seaside

For one of my projects at school, I am building an application using the Seaside web development framework. The project will eventually become a piece of courseware for a class I teach, and as such has requirements for authentication. I wanted to use our university’s authentication system to accomplish this. It took a fair amount of digging through the Seaside source code, and following the dead ends that the documentation provided (for example, WAAuthMain doesn’t even exist).

I decided to use the smalltalk LDAPLayer package to allow me to do what I needed to do. First order of business was to set up a way to do encrypted connections to the LDAP server. The LDAPLayer package does not support SSL connections out of the box (as far as i could tell), so rather than try to modify the source to allow it, I decided to run a local stunnel instance on my machine to provide a SSL proxy to the directory:


sudo stunnel -c -d 389 -r ad.uiuc.edu:636

I then can connect to localhost on the regular LDAP port, 389, and it will be securely tunneled to our campus’ Active Directory cluster.

The next step was to write an authenticator that would do the actual authentication. Mine turned out to look something like:

LDAPAuthenticator>>#verifyPassword: aPassword forUser: aUsername
	| dn req tempConn |
	aPassword ifNil: [^false].
	aPassword ifNil: [^false].
	dn := self getDNForUsername: aUsername .
	tempConn := LDAPConnection
		to: self class hostname
		port: self class port.
	req := tempConn
		bindAs: dn
		credentials: aPassword.
	[req wait] on: LDAPException do: [ :exception | tempConn disconnect. ^false ].
	tempConn disconnect.
	^true

I also implemented LDAPAuthenticator>>#getDNForUsername which converts a username into a fully qualified distinguished name for binding to the directory with. In addition, LDAPAuthenticator class>>#hostname and LDAPAuthenticator class>>#port return localhost and 139 respectively. It is important to note that the message implemented has to exactly match #verifyPassword:forUsername: and the message also needs to respond with false if either are nil (it was a bug that threw me off for a bit). Then its simply a matter of tying it all together in the initialize method of the application’s root component:

initialize
	"self initialize"
	| app authFilter |
	app := (WAAdmin register: self asApplicationAt: 'mypath')
		addLibrary: PTDeploymentLibrary;
		addLibrary: SUDeploymentLibrary;
		addLibrary: CTLibrary;
		yourself.
	app preferenceAt: #sessionClass put: MyCustomSession.
	authFilter := WAAuthenticationFilter new.
	authFilter realm: 'Active Directory. Use your username and Active Directory Password'.
	authFilter authenticator: LDAPAuthenticator new.
	app addFilter: authFilter .

Once that is complete, authentication works like a charm. Of course I could modify the authenticator to only allow members of certain groups, etc, but for my needs, letting anyone with valid credentials is quite alright. I also wanted to tie a user object representing the logged in user to my custom session, so I did the following to get the username of the authenticated user in my overridden #initialrequest in my root component:

username := self requestContext request user.

That took care of the whole mess for me…

Authenticating against UIUC Active Directory (AD) using Python

So in a follow up to my earlier post, here is the equivalent code to authenticate against UIUC Active Directory using Python:

ldap_auth.py:

import ldap

def authenticate(netid, password):
    server = "ldaps://ad.uiuc.edu:636"
    who = "CN="+netid+",OU=Campus Accounts,DC=ad,DC=uiuc,DC=edu"
    try:
        conn = ldap.initialize(server)
        ldap.set_option( ldap.OPT_X_TLS_DEMAND, True )
        ldap.set_option(ldap.OPT_X_TLS_REQUIRE_CERT,ldap.OPT_X_TLS_NEVER)
        result = conn.simple_bind_s(who,password)
    except ldap.LDAPError, e:
        return False
    conn.unbind()
    return True

print authenticate("cemeyer2", "password")

That’s it. As mentioned in my earlier post, I’m pretty sure that any application that authenticates this way would be looked down upon by CITES since the application in question would potentially be able to sniff user login information, so please only use this code in a personal testing environment, not for a deployed application unless approved by people much higher up than me.

Authenticating against UIUC Active Directory (AD) using Java

So in recent weeks I have become interested in means of authenticating users against sources such as Active Directory at UIUC. Although the UIUC domain controllers support Kerberos authentication, I have decided to implement a simpler LDAPS based method due to LDAP being included in the JRE. The code I wrote up looks something like:

LDAPAuth.java:

import java.util.Hashtable;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.InitialLdapContext;
import javax.naming.ldap.LdapContext;

public class LDAPAuth {

	public static void main(String[] args)
	{
		System.out.println(authenticate("cemeyer2","password"));
	}

	public static boolean authenticate(String netid, String password)
	{
		String userDN = findUserDN(netid);
		try
		{
			LdapContext ctx = createLDAPContext(userDN, password);
			ctx.close();
			return true;
		}
		catch(Exception e)
		{
			return false;
		}
	}

	private static String findUserDN(String netid)
	{
		return "CN="+netid+",OU=Campus Accounts,DC=ad,DC=uiuc,DC=edu";
	}

	private static LdapContext createLDAPContext(String bindDN, String bindPassword) throws NamingException
	{
		Hashtable env = new Hashtable();
		env.put("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
		env.put("java.naming.provider.url", "ldaps://ad.uiuc.edu/");
		env.put("java.naming.ldap.factory.socket", "TrustAllSSLSocketFactory");
		env.put("java.naming.security.protocol", "ssl");
		env.put("java.naming.security.principal", bindDN);
		env.put("java.naming.security.credentials", bindPassword);
		env.put("java.naming.security.authentication", "simple");
		env.put("com.sun.jndi.ldap.connect.pool", "true");
		LdapContext context = new InitialLdapContext(env, null);
		return context;
	}
}

And in the same package (default package in this example), TrustAllSSLSocketFactory.java:

import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.SocketFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

public class TrustAllSSLSocketFactory extends SSLSocketFactory
{
    private static class TrustAllTrustManager
        implements X509TrustManager
    {

        public void checkClientTrusted(X509Certificate ax509certificate[], String s1)
            throws CertificateException
        {
        }

        public void checkServerTrusted(X509Certificate ax509certificate[], String s1)
            throws CertificateException
        {
        }

        public X509Certificate[] getAcceptedIssuers()
        {
            return new X509Certificate[0];
        }

        private TrustAllTrustManager()
        {
        }

    }

    public TrustAllSSLSocketFactory()
    {
        try
        {
            SSLContext sslcontent = SSLContext.getInstance("TLS");
            sslcontent.init(null, new TrustManager[] {
                new TrustAllTrustManager()
            }, new SecureRandom());
            _factory = sslcontent.getSocketFactory();
        }
        catch(Throwable t)
        {
        	//do nothing
        }
    }

    public static SocketFactory getDefault()
    {
        return new TrustAllSSLSocketFactory();
    }

    public Socket createSocket(Socket socket, String s, int i, boolean flag)
        throws IOException
    {
        return _factory.createSocket(socket, s, i, flag);
    }

    public Socket createSocket(InetAddress inaddr, int i, InetAddress inaddr2, int j)
        throws IOException
    {
        return _factory.createSocket(inaddr, i, inaddr2, j);
    }

    public Socket createSocket(InetAddress inaddr, int i)
        throws IOException
    {
        return _factory.createSocket(inaddr, i);
    }

    public Socket createSocket(String s, int i, InetAddress inaddr, int j)
        throws IOException
    {
        return _factory.createSocket(s, i, inaddr, j);
    }

    public Socket createSocket(String s, int i)
        throws IOException
    {
        return _factory.createSocket(s, i);
    }

    public String[] getDefaultCipherSuites()
    {
        return _factory.getSupportedCipherSuites();
    }

    public String[] getSupportedCipherSuites()
    {
        return _factory.getSupportedCipherSuites();
    }

    private SSLSocketFactory _factory;

}

Once that’s done, using the static LDAPAuth.authenticate() method will allow you to authenticate users against UIUC Active Directory over LDAPS without the need for an AD service account. But I’m pretty sure that any application that authenticates this way would be looked down upon by CITES since the application in question would potentially be able to sniff user login information, so please only use this code in a personal testing environment, not for a deployed application unless approved by people much higher up than me.

Good Luck!