1
votes

We have a relational database with user information and hashed passwords that I'd like to serve up using LDAP for single sign-on purposes, and MyVD seems to be able to do everything we need. My first step was to get searches to work with no authentication whatsoever, and I've got the LDAP records coming in just fine for the users and their attributes, so I'm going back now and creating my own custom insert for the authentication mechanism which will basically hash the bind request's password and try to match it up to the hashed password in our database.

There is very little documentation as far as how to implement the particular details of the insert's operations (there's a fairly good overview about creating an insert in general), so I've been attempting to go off of existing source in the MyVD project as a basis for my code. Right now I continuously get the following Exception, which I've traced back through the project code, and still cannot figure out why it is occurring:

LDAPException: Could not bind to any services (49) Invalid Credentials
LDAPException: Server Message: [email protected],o=RBD,c=us
        at net.sourceforge.myvd.router.Router.bind(Router.java:164)
        at net.sourceforge.myvd.chain.BindInterceptorChain.nextBind(BindInterceptorChain.java:52)
        at net.sourceforge.myvd.inserts.routing.MasterReplicaRouter.bind(MasterReplicaRouter.java:109)
        at net.sourceforge.myvd.chain.BindInterceptorChain.nextBind(BindInterceptorChain.java:49)
        at net.sourceforge.myvd.inserts.DumpTransaction.bind(DumpTransaction.java:142)
        at net.sourceforge.myvd.chain.BindInterceptorChain.nextBind(BindInterceptorChain.java:49)
        at net.sourceforge.myvd.protocol.ldap.BindHandler.messageReceived(BindHandler.java:153)
        at net.sourceforge.myvd.protocol.ldap.LDAPOperation.messageReceived(LDAPOperation.java:84)
        at org.apache.mina.handler.demux.DemuxingIoHandler.messageReceived(DemuxingIoHandler.java:141)
        at net.sourceforge.myvd.protocol.ldap.LdapProtocolProvider$LdapProtocolHandler.messageReceived(LdapProtocolProvider.java:402)
        at org.apache.mina.common.support.AbstractIoFilterChain$TailFilter.messageReceived(AbstractIoFilterChain.java:570)
        at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
        at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
        at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
        at org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput.flush(SimpleProtocolDecoderOutput.java:58)
        at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:180)
        at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
        at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
        at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
        at org.apache.mina.filter.executor.ExecutorFilter.processEvent(ExecutorFilter.java:220)
        at org.apache.mina.filter.executor.ExecutorFilter$ProcessEventsRunnable.run(ExecutorFilter.java:264)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
        at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:51)
        at java.lang.Thread.run(Thread.java:662)

As you'll see with the insert class below, I'm just checking that the password is 123, and I can see from the log files that that's what's being sent to the class:

[2013-10-08 16:21:24,198][MyVD-2] DEBUG DBAuthenticator - USER: [email protected]
[2013-10-08 16:21:24,198][MyVD-2] DEBUG DBAuthenticator - PASSWORD: 123

So I'm at a loss, which is why I'm now posting this question ;) Here's my custom insert class that implements the Insert interface:

package com.foobar.myvd;

import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Properties;

import net.sourceforge.myvd.chain.AddInterceptorChain;
import net.sourceforge.myvd.chain.BindInterceptorChain;
import net.sourceforge.myvd.chain.CompareInterceptorChain;
import net.sourceforge.myvd.chain.DeleteInterceptorChain;
import net.sourceforge.myvd.chain.ExetendedOperationInterceptorChain;
import net.sourceforge.myvd.chain.ModifyInterceptorChain;
import net.sourceforge.myvd.chain.PostSearchCompleteInterceptorChain;
import net.sourceforge.myvd.chain.PostSearchEntryInterceptorChain;
import net.sourceforge.myvd.chain.RenameInterceptorChain;
import net.sourceforge.myvd.chain.SearchInterceptorChain;
import net.sourceforge.myvd.core.NameSpace;
import net.sourceforge.myvd.inserts.Insert;
import net.sourceforge.myvd.types.Attribute;
import net.sourceforge.myvd.types.Bool;
import net.sourceforge.myvd.types.DistinguishedName;
import net.sourceforge.myvd.types.Entry;
import net.sourceforge.myvd.types.ExtendedOperation;
import net.sourceforge.myvd.types.Filter;
import net.sourceforge.myvd.types.Int;
import net.sourceforge.myvd.types.Password;
import net.sourceforge.myvd.types.Results;

import org.apache.log4j.Logger;

import com.novell.ldap.LDAPConstraints;
import com.novell.ldap.LDAPException;
import com.novell.ldap.LDAPModification;
import com.novell.ldap.LDAPSearchConstraints;
import com.novell.ldap.util.RDN;

public class DBAuthenticator implements Insert {

    Logger logger = Logger.getLogger(DBAuthenticator.class);
    String name;

    @Override
    public void add(AddInterceptorChain arg0, Entry arg1, LDAPConstraints arg2)
            throws LDAPException {
        arg0.nextAdd(arg1, arg2);
    }

    @Override
    public void bind(BindInterceptorChain chain, DistinguishedName dn,
            Password password, LDAPConstraints constraints)
            throws LDAPException {
        String user = ((RDN) dn.getDN().getRDNs().get(0)).getValue();
        String passwordString = null;
        try {
            passwordString = new String(password.getValue(), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        logger.debug("USER: " + user);
        logger.debug("PASSWORD: " + passwordString);
        if (!passwordString.equals("123"))
            throw new LDAPException("Manual Invalid Credentials",
                    LDAPException.INVALID_CREDENTIALS,
                    "Manual Invalid Credentials");
        logger.debug("NO THROW");
        chain.nextBind(dn, password, constraints);
        logger.debug("nextBind CALLED.");

    }

    @Override
    public void compare(CompareInterceptorChain arg0, DistinguishedName arg1,
            Attribute arg2, LDAPConstraints arg3) throws LDAPException {
        arg0.nextCompare(arg1, arg2, arg3);
    }

    @Override
    public void configure(String name, Properties arg1, NameSpace arg2)
            throws LDAPException {
        this.name = name;
    }

    @Override
    public void delete(DeleteInterceptorChain arg0, DistinguishedName arg1,
            LDAPConstraints arg2) throws LDAPException {
        arg0.nextDelete(arg1, arg2);
    }

    @Override
    public void extendedOperation(ExetendedOperationInterceptorChain arg0,
            ExtendedOperation arg1, LDAPConstraints arg2) throws LDAPException {
        arg0.nextExtendedOperations(arg1, arg2);
    }

    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public void modify(ModifyInterceptorChain arg0, DistinguishedName arg1,
            ArrayList<LDAPModification> arg2, LDAPConstraints arg3)
            throws LDAPException {
        arg0.nextModify(arg1, arg2, arg3);
    }

    @Override
    public void postSearchComplete(PostSearchCompleteInterceptorChain arg0,
            DistinguishedName arg1, Int arg2, Filter arg3,
            ArrayList<Attribute> arg4, Bool arg5, LDAPSearchConstraints arg6)
            throws LDAPException {
    }

    @Override
    public void postSearchEntry(PostSearchEntryInterceptorChain arg0,
            Entry arg1, DistinguishedName arg2, Int arg3, Filter arg4,
            ArrayList<Attribute> arg5, Bool arg6, LDAPSearchConstraints arg7)
            throws LDAPException {
        arg0.nextPostSearchEntry(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
    }

    @Override
    public void rename(RenameInterceptorChain arg0, DistinguishedName arg1,
            DistinguishedName arg2, Bool arg3, LDAPConstraints arg4)
            throws LDAPException {
        arg0.nextRename(arg1, arg2, arg3, arg4);
    }

    @Override
    public void rename(RenameInterceptorChain arg0, DistinguishedName arg1,
            DistinguishedName arg2, DistinguishedName arg3, Bool arg4,
            LDAPConstraints arg5) throws LDAPException {
        arg0.nextRename(arg1, arg2, arg3, arg4, arg5);
    }

    @Override
    public void search(SearchInterceptorChain arg0, DistinguishedName arg1,
            Int arg2, Filter arg3, ArrayList<Attribute> arg4, Bool arg5,
            Results arg6, LDAPSearchConstraints arg7) throws LDAPException {
        arg0.nextSearch(arg1, arg2, arg3, arg4, arg5, arg6, arg7);
    }

    @Override
    public void shutdown() {
    }

}

And here is my current myvd.conf file that configures the server:

#Listen on port 389
server.listener.port=10389

#Listen on 636 using SSL
#server.secure.listener.port=636
#server.secure.keystore=/var/keystores/myvd.ks
#server.secure.keypass=secret

#Cnfigure global chains
server.globalChain=LogAllTransactions,router
server.globalChain.LogAllTransactions.className=net.sourceforge.myvd.inserts.DumpTransaction
server.globalChain.LogAllTransactions.config.logLevel=info
server.globalChain.LogAllTransactions.config.label=Global

server.globalChain.router.className=net.sourceforge.myvd.inserts.routing.MasterReplicaRouter
server.globalChain.router.config.specifyToInclude=false
server.globalChain.router.config.readOnly=Rbdconnect

#Configure namespaces
server.nameSpaces=Root,Readusers

#Define RootDSE
server.Root.chain=RootDSE
server.Root.nameSpace=
server.Root.weight=0
server.Root.RootDSE.className=net.sourceforge.myvd.inserts.RootDSE
server.Root.RootDSE.config.namingContexts=o=RBD,c=us

#Define rbdconnect namespace
server.Readusers.chain=dbauth,db
server.Readusers.nameSpace=o=RBD,c=us
server.Readusers.weight=100
server.Readusers.db.className=net.sourceforge.myvd.inserts.jdbc.JdbcInsert
server.Readusers.db.config.driver=org.postgresql.Driver
server.Readusers.db.config.url=jdbc:postgresql://localhost:5432/foo
server.Readusers.db.config.user=bar
server.Readusers.db.config.pass=bat
server.Readusers.db.config.rdn=uid
server.Readusers.db.config.mapping=uid=username,ou=ou,cn=employee_name,givenName=firstname,sn=lastname,mail=email,active=active
server.Readusers.db.config.objectClass=inetOrgPerson
server.Readusers.db.config.sql=SELECT ad_user.isactive AS active, username, ad_user.name AS employee_name, firstname, lastname, email, CASE WHEN ad_role.name IS NULL THEN 'Unknown' ELSE ad_role.name END AS ou FROM ad_user LEFT OUTER JOIN ad_role ON ad_user.default_ad_role_id = ad_role.ad_role_id

server.Readusers.dbauth.className=com.retailingwireless.myvd.DBAuthenticator

I would love someone to enlighten me as to what I'm missing because I've been staring at this for hours now, and (in my usual fashion) it's probably something simple and right under my nose. Thanks for taking the time!

1

1 Answers

0
votes

A couple of things:

  1. don't execute nextBind, that will move to the next insert and eventually to the db insert which I believe will throw an OperationNotSupported Exception

  2. You need to tell MyVD which adapters were a part of the authentication (which in 99% of cases is just the current one):

    chain.getSession().put(SessionVariables.BOUND_INTERCEPTORS,this.name);

Once you do those thing you should be good to go. The NTLM insert is a good example (net.sourceforge.myvd.inserts.jcifs.NTLMAuthenticator) since it only does authentication.