/*******************************************************************************

 * $Novell: MD5Bind.java,v 1.4 2005/01/17 14:48:34 $

 * Copyright (c) 2003 Novell, Inc. All Rights Reserved.

 *

 * THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND

 * TREATIES. USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE

 * AGREEMENT ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS

 * THIS WORK. PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS TO

 * DEVELOPER A ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S SAMPLE

 * CODE IN ITS PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION RIGHTS

 * TO MARKET, DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT OF

 * DEVELOPER'S PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR

 * DEVELOPER'S CUSTOMERS WITH RESPECT TO THIS CODE.

 *

 * $name:         MD5Bind.java

 * $description:  MD5Bind.java sample shows how to authenticate to an LDAP 

 *                server using a the DIGEST-MD5 SASL mechanism.

 *                

 *                The advantage of DIGEST-MD5 is that one can remain in clear 

 *                text mode but not have to send the password in clear text, 

 *                as in a simple bind. Nor does one need to deal with 

 *                generating and managing certificates. The eDirectory server 

 *                decodes this encrypted password and compares it with the 

 *                simplePassword attribute of the object on the server.

 *             

 *                The LDAP server must support the DIGEST-MD5 mechanism. The 

 *                SASL mechanisms supported by a server can be determined by 

 *                reading the supportedSASLMechanisms attribute of the rootdse 

 *                object. eDirectory uses the password stored in the 

 *                simplePassword attribute to perform a DIGEST-MD5 bind. This 

 *                value must be stored as clear text in order for the DIGEST-MD5

 *                bind to succeed. The simplePassword attribute can be set by 

 *                using the ICE import-export tool or through the SimplePassword

 *                ConsoleOne snap-in. The ConsoleOne snap-in always stores the 

 *                password as clear text.

 ******************************************************************************/



import com.novell.ldap.*;

import javax.security.auth.callback.*;

import com.novell.security.sasl.*;

import java.io.IOException;

import java.util.*;

import java.security.*;

import java.util.Enumeration;





public class MD5Bind

{



    public static void main( String[] args )

    {

        if (args.length != 4) {

            System.err.println("Usage:   java MD5Bind <host Name> "

                  + "<port number> <login dn> <password>" );

            System.err.println("Example: java MD5Bind Acme.com 389"

                       + " \"cn=Admin,o=Acme\" secret\n");

            System.exit(1);

        }



        int    ldapVersion = LDAPConnection.LDAP_V3;

        String ldapHost    = args[0];

        int    ldapPort    = Integer.parseInt(args[1]);

        String loginDN     = args[2];

        final String password       = args[3];

        String[]  mechanisms = {"DIGEST-MD5"};

        

        LDAPConnection lc  = new LDAPConnection();



        try { 

	        Security.addProvider(new 

	                         com.novell.sasl.client.SaslProvider());

	    }catch(Exception e) {

	        System.err.println("Error loading security provider (" + 

			   e.getMessage() + ")"); 

	    } 

	    

        try {

            // connect to the server

            lc.connect( ldapHost, ldapPort );

            // bind to the server

            lc.bind(loginDN,

               "dn: " + loginDN,

               mechanisms,

               null,

               new BindCallbackHandler(password));



            System.out.println("Digest-MD5 Sasl bind was successful.");

            lc.disconnect();

        }catch( LDAPException e ) {

            System.err.println("MD5Bind example failed");

            System.err.println( "Error: " + e.toString() );

            System.exit(1);

        }

        System.exit(0);

    }

    

}    

 

 

class BindCallbackHandler implements CallbackHandler{

     

        private char[] m_password;



        BindCallbackHandler(String  password)

        {

            m_password = new char[password.length()];

            password.getChars(0, password.length(), m_password, 0);

        }



        public void handle(Callback[] callbacks)

               throws IOException, UnsupportedCallbackException

        {

            for (int i=0; i<callbacks.length; i++)

            {

                if (callbacks[i] instanceof PasswordCallback)

                {

                    ((PasswordCallback)callbacks[i]).setPassword(m_password);

                }

                else if (callbacks[i] instanceof NameCallback)

                {

                    ((NameCallback)callbacks[i]).setName(

                    ((NameCallback)callbacks[i]).getDefaultName());

                }

                else if(callbacks[i] instanceof RealmCallback)

                {

                    ((RealmCallback)callbacks[i]).setText(

                    ((RealmCallback)callbacks[i]).getDefaultText());

                }

                else if (callbacks[i] instanceof RealmChoiceCallback)

                {

                    ((RealmChoiceCallback)callbacks[i]).setSelectedIndex(0);

                }

            }

        }

}

