You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by ka...@apache.org on 2010/09/16 20:46:06 UTC

svn commit: r997874 - in /directory: apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java

Author: kayyagari
Date: Thu Sep 16 18:46:06 2010
New Revision: 997874

URL: http://svn.apache.org/viewvc?rev=997874&view=rev
Log:
o added SASL GSSAPI mechanism support
o added a test
o some code cleanup

Modified:
    directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java
    directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java

Modified: directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java?rev=997874&r1=997873&r2=997874&view=diff
==============================================================================
--- directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java (original)
+++ directory/apacheds/trunk/server-integ/src/test/java/org/apache/directory/server/operations/bind/SaslBindIT.java Thu Sep 16 18:46:06 2010
@@ -30,6 +30,8 @@ import java.nio.ByteBuffer;
 import javax.naming.NamingEnumeration;
 import javax.naming.directory.Attribute;
 import javax.naming.directory.Attributes;
+import javax.naming.directory.BasicAttribute;
+import javax.naming.directory.BasicAttributes;
 import javax.naming.directory.DirContext;
 import javax.naming.directory.InitialDirContext;
 
@@ -37,6 +39,7 @@ import org.apache.commons.lang.ArrayUtil
 import org.apache.commons.net.SocketClient;
 import org.apache.directory.ldap.client.api.LdapConnection;
 import org.apache.directory.ldap.client.api.LdapNetworkConnection;
+import org.apache.directory.server.annotations.CreateKdcServer;
 import org.apache.directory.server.annotations.CreateLdapServer;
 import org.apache.directory.server.annotations.CreateTransport;
 import org.apache.directory.server.annotations.SaslMechanism;
@@ -48,14 +51,18 @@ import org.apache.directory.server.core.
 import org.apache.directory.server.core.integ.AbstractLdapTestUnit;
 import org.apache.directory.server.core.integ.FrameworkRunner;
 import org.apache.directory.server.core.kerberos.KeyDerivationInterceptor;
+import org.apache.directory.server.kerberos.shared.store.KerberosAttribute;
 import org.apache.directory.server.ldap.handlers.bind.cramMD5.CramMd5MechanismHandler;
 import org.apache.directory.server.ldap.handlers.bind.digestMD5.DigestMd5MechanismHandler;
 import org.apache.directory.server.ldap.handlers.bind.gssapi.GssapiMechanismHandler;
 import org.apache.directory.server.ldap.handlers.bind.ntlm.NtlmMechanismHandler;
 import org.apache.directory.server.ldap.handlers.bind.plain.PlainMechanismHandler;
 import org.apache.directory.server.ldap.handlers.extended.StoredProcedureExtendedOperationHandler;
+import org.apache.directory.shared.ldap.constants.SchemaConstants;
 import org.apache.directory.shared.ldap.constants.SupportedSaslMechanisms;
+import org.apache.directory.shared.ldap.entry.DefaultEntry;
 import org.apache.directory.shared.ldap.entry.Entry;
+import org.apache.directory.shared.ldap.exception.LdapException;
 import org.apache.directory.shared.ldap.message.BindRequest;
 import org.apache.directory.shared.ldap.message.BindRequestImpl;
 import org.apache.directory.shared.ldap.message.BindResponse;
@@ -96,7 +103,38 @@ import org.slf4j.LoggerFactory;
         "krb5PrincipalName: hnelson@EXAMPLE.COM",
         "krb5KeyVersionNumber: 0",
         "cn: Horatio Nelson",
-        "sn: Nelson" })
+        "sn: Nelson",
+    
+        // krbtgt
+        "dn: uid=krbtgt,ou=users,dc=example,dc=com",
+        "objectClass: inetOrgPerson",
+        "objectClass: organizationalPerson",
+        "objectClass: person",
+        "objectClass: krb5principal",
+        "objectClass: krb5kdcentry",
+        "objectClass: top",
+        "uid: krbtgt",
+        "userPassword: secret",
+        "krb5PrincipalName: krbtgt/EXAMPLE.COM@EXAMPLE.COM",
+        "krb5KeyVersionNumber: 0",
+        "cn: KDC Service",
+        "sn: Service",
+        
+        // ldap 
+        "dn: uid=ldap,ou=users,dc=example,dc=com",
+        "objectClass: inetOrgPerson",
+        "objectClass: organizationalPerson",
+        "objectClass: person",
+        "objectClass: krb5principal",
+        "objectClass: krb5kdcentry",
+        "objectClass: top",
+        "uid: ldap",
+        "userPassword: randall",
+        "krb5PrincipalName: ldap/localhost@EXAMPLE.COM",
+        "krb5KeyVersionNumber: 0",
+        "cn: LDAP Service",
+        "sn: Service"
+    })
 @CreateDS(allowAnonAccess = false, name = "SaslBindIT-class", partitions =
     { @CreatePartition(name = "example", suffix = "dc=example,dc=com", contextEntry = @ContextEntry(entryLdif = "dn: dc=example,dc=com\n"
         + "dc: example\n" + "objectClass: top\n" + "objectClass: domain\n\n"), indexes =
@@ -104,7 +142,7 @@ import org.slf4j.LoggerFactory;
 additionalInterceptors = { KeyDerivationInterceptor.class }
 )
 @CreateLdapServer(transports =
-    { @CreateTransport(protocol = "LDAP") }, saslHost = "localhost", saslMechanisms =
+    { @CreateTransport(protocol = "LDAP") }, saslHost = "localhost", saslPrincipal="ldap/localhost@EXAMPLE.COM", saslMechanisms =
     { @SaslMechanism(name = SupportedSaslMechanisms.PLAIN, implClass = PlainMechanismHandler.class),
         @SaslMechanism(name = SupportedSaslMechanisms.CRAM_MD5, implClass = CramMd5MechanismHandler.class),
         @SaslMechanism(name = SupportedSaslMechanisms.DIGEST_MD5, implClass = DigestMd5MechanismHandler.class),
@@ -112,6 +150,12 @@ additionalInterceptors = { KeyDerivation
         @SaslMechanism(name = SupportedSaslMechanisms.NTLM, implClass = NtlmMechanismHandler.class),
         @SaslMechanism(name = SupportedSaslMechanisms.GSS_SPNEGO, implClass = NtlmMechanismHandler.class) }, extendedOpHandlers =
     { StoredProcedureExtendedOperationHandler.class }, ntlmProvider = BogusNtlmProvider.class)
+@CreateKdcServer ( 
+    transports = 
+    {
+        @CreateTransport( protocol = "UDP", port = 6088 ),
+        @CreateTransport( protocol = "TCP", port = 6088 )
+    })
 public class SaslBindIT extends AbstractLdapTestUnit
 {
 
@@ -267,7 +311,6 @@ public class SaslBindIT extends Abstract
         {
             DN userDn = new DN( "uid=hnelson,ou=users,dc=example,dc=com" );
             LdapNetworkConnection connection = new LdapNetworkConnection( "localhost", ldapServer.getPort() );
-            connection.setTimeOut( Integer.MAX_VALUE );
 
             BindResponse resp = connection.bindDigestMd5( userDn.getName(), "secret", null, ldapServer.getSaslRealms()
                 .get( 0 ) );
@@ -286,6 +329,32 @@ public class SaslBindIT extends Abstract
 
 
     /**
+     * GSSAPI test
+     */
+    @Test
+    public void testSaslGssApiBind() throws Exception
+    {
+        try
+        {
+            DN userDn = new DN( "uid=hnelson,ou=users,dc=example,dc=com" );
+            LdapNetworkConnection connection = new LdapNetworkConnection( "localhost", ldapServer.getPort() );
+
+            BindResponse resp = connection.bindGssApi( userDn.getName(), "secret", ldapServer.getSaslRealms().get( 0 ).toUpperCase(), "localhost", 6088 );
+            assertEquals( ResultCodeEnum.SUCCESS, resp.getLdapResult().getResultCode() );
+
+            Entry entry = connection.lookup( userDn );
+            assertEquals( "hnelson", entry.get( "uid" ).getString() );
+
+            connection.close();
+        }
+        catch ( Exception e )
+        {
+            fail( "Should not have caught exception." );
+        }
+    }
+
+    
+    /**
      * Tests to make sure DIGEST-MD5 binds below the RootDSE fail if the realm is bad.
      */
     @Test
@@ -295,7 +364,6 @@ public class SaslBindIT extends Abstract
         {
             DN userDn = new DN( "uid=hnelson,ou=users,dc=example,dc=com" );
             LdapNetworkConnection connection = new LdapNetworkConnection( "localhost", ldapServer.getPort() );
-            connection.setTimeOut( Integer.MAX_VALUE );
 
             BindResponse resp = connection.bindDigestMd5( userDn.getName(), "secret", null, "badrealm.com" );
             assertEquals( ResultCodeEnum.INVALID_CREDENTIALS, resp.getLdapResult().getResultCode() );
@@ -320,7 +388,6 @@ public class SaslBindIT extends Abstract
         {
             DN userDn = new DN( "uid=hnelson,ou=users,dc=example,dc=com" );
             LdapNetworkConnection connection = new LdapNetworkConnection( "localhost", ldapServer.getPort() );
-            connection.setTimeOut( Integer.MAX_VALUE );
 
             BindResponse resp = connection.bindDigestMd5( userDn.getName(), "badsecret", null, ldapServer
                 .getSaslRealms().get( 0 ) );
@@ -518,4 +585,20 @@ public class SaslBindIT extends Abstract
         return provider;
     }
 
+    
+    ////////////////////////
+    protected Entry getPrincipalAttributes( String dn, String sn, String cn, String uid, String userPassword, String principal ) throws LdapException
+    {
+        Entry entry = new DefaultEntry( new DN( dn ) );
+        entry.add( SchemaConstants.OBJECT_CLASS_AT, "person", "inetOrgPerson", "krb5principal", "krb5kdcentry" );
+        entry.add( SchemaConstants.CN_AT, cn );
+        entry.add( SchemaConstants.SN_AT, sn );
+        entry.add( SchemaConstants.UID_AT, uid );
+        entry.add( SchemaConstants.USER_PASSWORD_AT, userPassword );
+        entry.add( KerberosAttribute.KRB5_PRINCIPAL_NAME_AT, principal );
+        entry.add( KerberosAttribute.KRB5_KEY_VERSION_NUMBER_AT, "0" );
+
+        return entry;
+    }
+
 }

Modified: directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java
URL: http://svn.apache.org/viewvc/directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java?rev=997874&r1=997873&r2=997874&view=diff
==============================================================================
--- directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java (original)
+++ directory/shared/trunk/ldap-client-api/src/main/java/org/apache/directory/ldap/client/api/LdapNetworkConnection.java Thu Sep 16 18:46:06 2010
@@ -21,9 +21,11 @@ package org.apache.directory.ldap.client
 
 
 import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.security.PrivilegedExceptionAction;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -37,6 +39,9 @@ import java.util.concurrent.atomic.Atomi
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.net.ssl.SSLContext;
+import javax.security.auth.Subject;
+import javax.security.auth.login.Configuration;
+import javax.security.auth.login.LoginContext;
 import javax.security.sasl.Sasl;
 import javax.security.sasl.SaslClient;
 
@@ -1205,7 +1210,8 @@ public class LdapNetworkConnection exten
         throws LdapException,
         IOException
     {
-        BindFuture bindFuture = bindSasl( name, credentials, SupportedSaslMechanisms.DIGEST_MD5, authzId, realmName, ctrls );
+        BindFuture bindFuture = bindSasl( name, credentials, SupportedSaslMechanisms.DIGEST_MD5, authzId, realmName,
+            ctrls );
 
         try
         {
@@ -1235,6 +1241,64 @@ public class LdapNetworkConnection exten
 
 
     /**
+     * @see #bindGssApi(String, byte[], String, String, int, Control...)
+     */
+    public BindResponse bindGssApi( String name, String credentials, String realmName, String kdcHost, int kdcPort, Control... ctrls )
+    throws LdapException, IOException
+    {
+        return bindGssApi( name, StringTools.getBytesUtf8( credentials ), realmName, kdcHost, kdcPort, ctrls );
+    }
+    
+    
+    /**
+     * bind to the ldap server using GSSAPI sasl mechanism
+     *
+     * @param name the DN of the user entry
+     * @param credentials the credentials of the user
+     * @param realmName name of the kerberos realm in which the given user entry is present
+     * @param kdcHost the host name of the KDC server
+     * @param kdcPort the port of the KDC server
+     * @param ctrls controls to be passed along with the bind request
+     * @return response of this bind operation
+     * @throws LdapException
+     * @throws IOException
+     */
+    public BindResponse bindGssApi( String name, byte[] credentials, String realmName, String kdcHost, int kdcPort, Control... ctrls )
+        throws LdapException, IOException
+    {
+        BindRequest bindReq = createBindRequest( name, credentials, SupportedSaslMechanisms.GSSAPI, ctrls );
+        
+        String krbConfPath = createKrbConfFile( realmName, kdcHost, kdcPort );
+        System.setProperty( "java.security.krb5.conf", krbConfPath );
+
+        Configuration.setConfiguration( new Krb5LoginConfiguration() );
+        System.setProperty( "javax.security.auth.useSubjectCredsOnly", "true" );
+
+        final SaslRequest saslReq = new SaslRequest( bindReq );
+
+        try
+        {
+            LoginContext lc = new LoginContext( "ldapnetworkconnection", new SaslCallbackHandler( saslReq ) );
+            lc.login();
+
+            BindFuture future = ( BindFuture ) Subject.doAs( lc.getSubject(), new PrivilegedExceptionAction<Object>()
+            {
+                public Object run() throws Exception
+                {
+                    return bindSasl( saslReq );
+                }
+            } );
+
+            return future.get();
+        }
+        catch ( Exception e )
+        {
+            throw new LdapException( e );
+        }
+    }
+
+
+    /**
      * {@inheritDoc}
      */
     public Cursor<Response> search( DN baseDn, String filter, SearchScope scope, String... attributes )
@@ -3364,10 +3428,7 @@ public class LdapNetworkConnection exten
         Control... ctrls )
         throws LdapException, IOException
     {
-        BindRequest bindReq = createBindRequest( name, credentials );
-        bindReq.setSaslMechanism( saslMech );
-        bindReq.setSimple( false );
-        bindReq.addAllControls( ctrls );
+        BindRequest bindReq = createBindRequest( name, credentials, saslMech, ctrls );
 
         SaslRequest saslReq = new SaslRequest( bindReq );
         saslReq.setRealmName( realmName );
@@ -3507,4 +3568,59 @@ public class LdapNetworkConnection exten
             throw new LdapException( TIME_OUT_ERROR );
         }
     }
+
+    
+    /**
+     * method to write the kerberos config in the standard MIT kerberos format
+     * 
+     * This is required cause the JGSS api is not able to recognize the port value set 
+     * in the system property java.security.krb5.kdc this issue makes it impossible
+     * to set a kdc running non standard port(other than 88)
+     *  
+     * e.g localhost:6088
+     * 
+     * [libdefaults]
+     *     default_realm = EXAMPLE.COM
+     *
+     * [realms]
+     *     EXAMPLE.COM = {
+     *         kdc = localhost:6088
+     *     }
+     *     
+     * @return the full path of the config file
+     */
+    private String createKrbConfFile( String realmName, String kdcHost, int kdcPort ) throws IOException
+    {
+        StringBuilder sb = new StringBuilder();
+
+        sb.append( "[libdefaults]" )
+            .append( "\n\t" );
+        sb.append( "default_realm = " )
+            .append( realmName )
+            .append( "\n" );
+
+        sb.append( "[realms]" )
+            .append( "\n\t" );
+
+        sb.append( realmName )
+            .append( " = {" )
+            .append( "\n\t\t" );
+        sb.append( "kdc = " )
+            .append( kdcHost )
+            .append( ":" )
+            .append( kdcPort )
+            .append( "\n\t}\n" );;
+
+        File krb5Conf = File.createTempFile( "client-api-krb5", ".conf" );
+        krb5Conf.deleteOnExit();
+        FileWriter fw = new FileWriter( krb5Conf );
+        fw.write( sb.toString() );
+        fw.close();
+
+        String krbConfPath = krb5Conf.getAbsolutePath();
+
+        LOG.debug( "krb config file created at {}", krbConfPath );
+
+        return krbConfPath;
+    }
 }