JAAS LoginModule for Linux Authentication

I was looking for a possibility to use the Linux passwd database authentication in Java in order to authenticate a user with a password against the local linux passwd database. Soon I realized that this is not possible out-of-the-box because there is no LoginModule for that. The project http://sourceforge.net/projects/jaas-pam/ seemed exactly the right thing for that but unfortunately it is outdated and lets Sun JRE 1.6 and IBM JRE 1.5 segfault. No matter if using the supplied binary lib or with a recompiled one, no avail.

So I implemented my own one, a JAAS LoginModule that authenticates against a local SSH Server using the sshj project. It opens up a connection to your local SSH server which must be running on the machine you want to authenticate against. Then it tries to log in the supplied user. If it succeeds the supplied username password pair is vaild on the server. You need the sshj library, mavens artifact is net.schmitzz.sshj.

import java.io.IOException;
import java.util.Map;

import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.NameCallback;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.login.LoginException;
import javax.security.auth.spi.LoginModule;

import net.schmizz.sshj.SSHClient;

public class SshLoginModule implements LoginModule {
	private CallbackHandler handler;
	private Subject subject;
	private String username;

	/**
	 * @see javax.security.auth.spi.LoginModule#abort()
	 */
	@Override
	public boolean abort() throws LoginException {
		return false;
	}

	/**
	 * @see javax.security.auth.spi.LoginModule#commit()
	 */
	@Override
	public boolean commit() throws LoginException {
		try {
			final DummyUserPrincipal user = new DummyUserPrincipal(
					username);
			final DummyRolePrincipal role = new DummyRolePrincipal(
					"admin");

			subject.getPrincipals().add(user);
			subject.getPrincipals().add(role);
			return true;

		} catch (final Exception e) {
			throw new LoginException(e.getMessage());
		}
	}

	/**
	 * @see javax.security.auth.spi.LoginModule#initialize(javax.security.auth.Subject,
	 *      javax.security.auth.callback.CallbackHandler, java.util.Map,
	 *      java.util.Map)
	 */
	@Override
	public void initialize(Subject subject, CallbackHandler callbackHandler, Map sharedState, Map options) {

		handler = callbackHandler;
		this.subject = subject;
	}

	/**
	 * @see javax.security.auth.spi.LoginModule#login()
	 */
	@Override
	public boolean login() throws LoginException {
		final Callback[] callbacks = new Callback[2];
		callbacks[0] = new NameCallback("username");
		callbacks[1] = new PasswordCallback("password", true);

		final SSHClient sshClient = new SSHClient();
		final String fingerprint = "33:**:**:**:..."; //15 hex digit fingerprint of your server.
		final String hostname = "localhost";

		try {
			handler.handle(callbacks);
			final String name = ((NameCallback) callbacks[0])
					.getName();
			final String password = String
					.valueOf(((PasswordCallback) callbacks[1])
							.getPassword());

			sshClient.addHostKeyVerifier(hostname, 22, fingerprint);
			sshClient.connect(hostname, 22);
			sshClient.authPassword(name, password);

			if (!sshClient.isAuthenticated()) {
				throw new LoginException(
						"Authentication failed for unknown reason.");
			}
			username = name;
			return true;

		} catch (final IOException e) {
			throw new LoginException("Authentication failed: "
					+ e.getMessage());
		} catch (final UnsupportedCallbackException e) {
			throw new LoginException("Authentication failed: "
					+ e.getMessage());
		} finally {
			try {
				sshClient.disconnect();
			} catch (final IOException e) {
				throw new LoginException("Authentication failed: "
						+ e.getMessage());
			}
		}
	}

	/**
	 * @see javax.security.auth.spi.LoginModule#logout()
	 */
	@Override
	public boolean logout() throws LoginException {
		try {
			final DummyUserPrincipal user = new DummyUserPrincipal(
					username);
			final DummyRolePrincipal role = new DummyRolePrincipal(
					"admin");
			subject.getPrincipals().remove(user);
			subject.getPrincipals().remove(role);
			return true;
		} catch (final Exception e) {
			throw new LoginException(e.getMessage());
		}
	}
}

Leave a Reply

Your email address will not be published. Required fields are marked *