You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@directory.apache.org by el...@apache.org on 2013/08/11 22:15:11 UTC

svn commit: r1512996 - in /directory/apacheds/trunk/kerberos-codec/src: main/java/org/apache/directory/server/kerberos/shared/keytab/ test/java/org/apache/directory/server/kerberos/shared/keytab/

Author: elecharny
Date: Sun Aug 11 20:15:10 2013
New Revision: 1512996

URL: http://svn.apache.org/r1512996
Log:
Fix for DIRSERVER-1882

Modified:
    directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/Keytab.java
    directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEncoder.java
    directory/apacheds/trunk/kerberos-codec/src/test/java/org/apache/directory/server/kerberos/shared/keytab/KeytabTest.java

Modified: directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/Keytab.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/Keytab.java?rev=1512996&r1=1512995&r2=1512996&view=diff
==============================================================================
--- directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/Keytab.java (original)
+++ directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/Keytab.java Sun Aug 11 20:15:10 2013
@@ -35,7 +35,38 @@ import org.apache.directory.server.i18n.
 
 
 /**
- * Keytab file.
+ * Keytab file. The format is the following :
+ * <pre>
+ * { 
+ *   version : 2 bytes (0x05 0x02)
+ *   keytabEntry*
+ * }
+ *
+ * keytab_entry 
+ * {
+ *     size : int
+ *     numComponents :  short
+ *     realm : countedOctetString
+ *     components[numComponents] : countedOctetString
+ *     nameType : int
+ *     timestamp : int
+ *     vno8 : byte
+ *     key : keyBlock
+ *     vno : int // only present if >= 4 bytes left in entry
+ * };
+ *
+ * keyblock 
+ * {
+ *     type : int
+ *     data : countedOctetString
+ * }
+ *
+ * countedOctetString 
+ * {
+ *     length : short
+ *     data[length] : bytes
+ * }
+ *
  *
  * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
  */
@@ -44,16 +75,22 @@ public class Keytab
     /**
      * Byte array constant for keytab file format 5.1.
      */
-    public static final byte[] VERSION_51 = new byte[]
+    public static final byte[] VERSION_0X501_BYTES = new byte[]
         { ( byte ) 0x05, ( byte ) 0x01 };
 
+    // Format 0x0501
+    public static final short VERSION_0X501 = 0x0501;
+
     /**
      * Byte array constant for keytab file format 5.2.
      */
-    public static final byte[] VERSION_52 = new byte[]
+    public static final byte[] VERSION_0X502_BYTES = new byte[]
         { ( byte ) 0x05, ( byte ) 0x02 };
 
-    private byte[] keytabVersion = VERSION_52;
+    // Format 0x0502
+    public static final short VERSION_0X502 = 0x0502;
+
+    private byte[] keytabVersion = VERSION_0X502_BYTES;
     private List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
 
 
@@ -218,7 +255,7 @@ public class Keytab
 
         // Close the input stream and return bytes.
         is.close();
-        
+
         return bytes;
     }
 

Modified: directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEncoder.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEncoder.java?rev=1512996&r1=1512995&r2=1512996&view=diff
==============================================================================
--- directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEncoder.java (original)
+++ directory/apacheds/trunk/kerberos-codec/src/main/java/org/apache/directory/server/kerberos/shared/keytab/KeytabEncoder.java Sun Aug 11 20:15:10 2013
@@ -20,8 +20,9 @@
 package org.apache.directory.server.kerberos.shared.keytab;
 
 
+import java.io.UnsupportedEncodingException;
 import java.nio.ByteBuffer;
-import java.util.Iterator;
+import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.directory.shared.kerberos.components.EncryptionKey;
@@ -35,6 +36,30 @@ import org.apache.directory.shared.kerbe
 class KeytabEncoder
 {
     /**
+     * Tells if the keytabCersion is 0x0501 or 0x0502
+     */
+    private short getKeytabVersion( byte[] version )
+    {
+        if ( ( version != null ) && ( version.length == 2 ) && ( version[0] == 0x05 ) )
+        {
+            switch ( version[1] )
+            {
+                case 0x01:
+                    return Keytab.VERSION_0X501;
+
+                case 0x02:
+                    return Keytab.VERSION_0X502;
+
+                default:
+                    return -1;
+            }
+        }
+
+        return -1;
+    }
+
+
+    /**
      * Write the keytab version and entries into a {@link ByteBuffer}.
      *
      * @param keytabVersion
@@ -43,45 +68,56 @@ class KeytabEncoder
      */
     ByteBuffer write( byte[] keytabVersion, List<KeytabEntry> entries )
     {
-        ByteBuffer buffer = ByteBuffer.allocate( 512 );
-        putKeytabVersion( buffer, keytabVersion );
-        putKeytabEntries( buffer, entries );
-        buffer.flip();
+        List<ByteBuffer> keytabEntryBuffers = new ArrayList<ByteBuffer>();;
+        short version = getKeytabVersion( keytabVersion );
 
-        return buffer;
-    }
+        int buffersSize = encodeKeytabEntries( keytabEntryBuffers, version, entries );
 
+        ByteBuffer buffer = ByteBuffer.allocate(
+            keytabVersion.length + buffersSize );
 
-    /**
-     * Encode the 16-bit file format version.  This
-     * keytab reader currently only support verision 5.2.
-     */
-    private void putKeytabVersion( ByteBuffer buffer, byte[] version )
-    {
-        buffer.put( version );
+        // The keytab version (0x0502 or 0x5001)
+        buffer.put( keytabVersion );
+
+        for ( ByteBuffer keytabEntryBuffer : keytabEntryBuffers )
+        {
+            // The buffer
+            buffer.put( keytabEntryBuffer );
+        }
+
+        buffer.flip();
+
+        return buffer;
     }
 
 
     /**
-     * Encode the keytab entries.
+     * Encode the keytab entries. Each entry stores :
+     * - the size
+     * - the principal name
+     * - the type (int, 4 bytes)
+     * - the timestamp (int, 4 bytes)
+     * - the key version (1 byte)
+     * - the key 
      *
      * @param buffer
      * @param entries
      */
-    private void putKeytabEntries( ByteBuffer buffer, List<KeytabEntry> entries )
+    private int encodeKeytabEntries( List<ByteBuffer> buffers, short version, List<KeytabEntry> entries )
     {
-        Iterator<KeytabEntry> iterator = entries.iterator();
+        int size = 0;
 
-        while ( iterator.hasNext() )
+        for ( KeytabEntry keytabEntry : entries )
         {
-            ByteBuffer entryBuffer = putKeytabEntry( iterator.next() );
-            int size = entryBuffer.position();
+            ByteBuffer entryBuffer = encodeKeytabEntry( version, keytabEntry );
 
-            entryBuffer.flip();
+            buffers.add( entryBuffer );
 
-            buffer.putInt( size );
-            buffer.put( entryBuffer );
+            // The buffer size
+            size += entryBuffer.limit();
         }
+
+        return size;
     }
 
 
@@ -89,19 +125,49 @@ class KeytabEncoder
      * Encode a "keytab entry," which consists of a principal name,
      * principal type, key version number, and key material.
      */
-    private ByteBuffer putKeytabEntry( KeytabEntry entry )
+    private ByteBuffer encodeKeytabEntry( short version, KeytabEntry entry )
     {
-        ByteBuffer buffer = ByteBuffer.allocate( 100 );
+        // Compute the principalName encoding
+        ByteBuffer principalNameBuffer = encodePrincipalName( version, entry.getPrincipalName() );
+
+        // Compute the keyblock encoding
+        ByteBuffer keyBlockBuffer = encodeKeyBlock( entry.getKey() );
 
-        putPrincipalName( buffer, entry.getPrincipalName() );
+        int bufferSize =
+            4 + // size
+                principalNameBuffer.limit() + // principalName size
+                4 + // timeStamp
+                1 + // keyVersion
+                keyBlockBuffer.limit(); // keyBlock size
 
-        buffer.putInt( ( int ) entry.getPrincipalType() );
+        if ( version == Keytab.VERSION_0X502 )
+        {
+            bufferSize += 4; // Add the principal NameType only for version 0x502
+        }
+
+        ByteBuffer buffer = ByteBuffer.allocate( bufferSize );
+
+        // Store the size
+        buffer.putInt( bufferSize - 4 );
+
+        // Store the principalNames
+        buffer.put( principalNameBuffer );
+
+        // Store the principal type if version == 0x0502
+        if ( version == Keytab.VERSION_0X502 )
+        {
+            buffer.putInt( ( int ) entry.getPrincipalType() );
+        }
 
+        // Store the timeStamp 
         buffer.putInt( ( int ) ( entry.getTimeStamp().getTime() / 1000 ) );
 
+        // Store the key version
         buffer.put( entry.getKeyVersion() );
 
-        putKeyBlock( buffer, entry.getKey() );
+        // Store the KeyBlock
+        buffer.put( keyBlockBuffer );
+        buffer.flip();
 
         return buffer;
     }
@@ -113,34 +179,78 @@ class KeytabEncoder
      * @param buffer
      * @param principalName
      */
-    private void putPrincipalName( ByteBuffer buffer, String principalName )
+    private ByteBuffer encodePrincipalName( short version, String principalName )
     {
         String[] split = principalName.split( "@" );
-        String nameComponent = split[0];
+        String nameComponentPart = split[0];
         String realm = split[1];
 
-        String[] nameComponents = nameComponent.split( "/" );
+        String[] nameComponents = nameComponentPart.split( "/" );
+
+        // Compute the size of the buffer
+        List<byte[]> strings = new ArrayList<byte[]>();
+
+        // Initialize the size with the number of components' size
+        int size = 2;
+
+        size += encodeCountedString( strings, realm );
+
+        // compute NameComponents
+        for ( String nameComponent : nameComponents )
+        {
+            size += encodeCountedString( strings, nameComponent );
+        }
 
-        // increment for v1
-        buffer.putShort( ( short ) nameComponents.length );
+        ByteBuffer buffer = ByteBuffer.allocate( size );
 
-        putCountedString( buffer, realm );
-        // write components
+        // Now, write the data into the buffer
+        // store the numComponents
+        if ( version == Keytab.VERSION_0X501 )
+        {
+            // increment for version 0x0501
+            buffer.putShort( ( short ) ( nameComponents.length + 1 ) );
+        }
+        else
+        {
+            // Version = OxO502
+            buffer.putShort( ( short ) ( nameComponents.length ) );
+        }
 
-        for ( int ii = 0; ii < nameComponents.length; ii++ )
+        // Store the realm and the nameComponents
+        for ( byte[] string : strings )
         {
-            putCountedString( buffer, nameComponents[ii] );
+            buffer.putShort( ( short ) ( string.length ) );
+            buffer.put( string );
         }
+
+        buffer.flip();
+
+        return buffer;
     }
 
 
     /**
      * Encode a 16-bit encryption type and symmetric key material.
+     * 
+     * We store the KeyType value ( a short ) and the KeyValue ( a length
+     * on a short and the bytes )
      */
-    private void putKeyBlock( ByteBuffer buffer, EncryptionKey key )
+    private ByteBuffer encodeKeyBlock( EncryptionKey key )
     {
+        byte[] keyBytes = key.getKeyValue();
+        int size = 2 + 2 + keyBytes.length; // type, length, data
+        ByteBuffer buffer = ByteBuffer.allocate( size );
+
+        // The type
         buffer.putShort( ( short ) key.getKeyType().getValue() );
-        putCountedBytes( buffer, key.getKeyValue() );
+
+        // Use a prefixed 16-bit length to encode raw bytes.
+        buffer.putShort( ( short ) keyBytes.length );
+        buffer.put( keyBytes );
+
+        buffer.flip();
+
+        return buffer;
     }
 
 
@@ -148,20 +258,18 @@ class KeytabEncoder
      * Use a prefixed 16-bit length to encode a String.  Realm and name
      * components are ASCII encoded text with no zero terminator.
      */
-    private void putCountedString( ByteBuffer buffer, String string )
+    private short encodeCountedString( List<byte[]> nameComponentBytes, String string )
     {
-        byte[] data = string.getBytes();
-        buffer.putShort( ( short ) data.length );
-        buffer.put( data );
-    }
-
+        try
+        {
+            byte[] data = string.getBytes( "US-ASCII" );
+            nameComponentBytes.add( data );
 
-    /**
-     * Use a prefixed 16-bit length to encode raw bytes.
-     */
-    private void putCountedBytes( ByteBuffer buffer, byte[] data )
-    {
-        buffer.putShort( ( short ) data.length );
-        buffer.put( data );
+            return ( short ) ( data.length + 2 );
+        }
+        catch ( UnsupportedEncodingException uee )
+        {
+            throw new RuntimeException( uee.getMessage(), uee );
+        }
     }
 }

Modified: directory/apacheds/trunk/kerberos-codec/src/test/java/org/apache/directory/server/kerberos/shared/keytab/KeytabTest.java
URL: http://svn.apache.org/viewvc/directory/apacheds/trunk/kerberos-codec/src/test/java/org/apache/directory/server/kerberos/shared/keytab/KeytabTest.java?rev=1512996&r1=1512995&r2=1512996&view=diff
==============================================================================
--- directory/apacheds/trunk/kerberos-codec/src/test/java/org/apache/directory/server/kerberos/shared/keytab/KeytabTest.java (original)
+++ directory/apacheds/trunk/kerberos-codec/src/test/java/org/apache/directory/server/kerberos/shared/keytab/KeytabTest.java Sun Aug 11 20:15:10 2013
@@ -35,6 +35,7 @@ import java.util.Map;
 
 import javax.crypto.spec.DESKeySpec;
 
+import org.apache.directory.api.util.Strings;
 import org.apache.directory.server.kerberos.shared.crypto.encryption.KerberosKeyFactory;
 import org.apache.directory.shared.kerberos.KerberosTime;
 import org.apache.directory.shared.kerberos.KerberosUtils;
@@ -46,6 +47,7 @@ import org.junit.runner.RunWith;
 import com.mycila.junit.concurrent.Concurrency;
 import com.mycila.junit.concurrent.ConcurrentJunitRunner;
 
+
 /**
  * Tests 'keytab' formatted files.
  * 
@@ -92,7 +94,7 @@ public class KeytabTest
     {
         Keytab keytab = Keytab.read( keytab1 );
 
-        assertTrue( "Keytab version", Arrays.equals( Keytab.VERSION_52, keytab.getKeytabVersion() ) );
+        assertTrue( "Keytab version", Arrays.equals( Keytab.VERSION_0X502_BYTES, keytab.getKeytabVersion() ) );
         assertEquals( "Entries size", 1, keytab.getEntries().size() );
 
         KeytabEntry entry = keytab.getEntries().get( 0 );
@@ -119,7 +121,7 @@ public class KeytabTest
     {
         Keytab keytab = Keytab.read( keytab2 );
 
-        assertTrue( "Keytab version", Arrays.equals( Keytab.VERSION_52, keytab.getKeytabVersion() ) );
+        assertTrue( "Keytab version", Arrays.equals( Keytab.VERSION_0X502_BYTES, keytab.getKeytabVersion() ) );
         assertEquals( "Entries size", 1, keytab.getEntries().size() );
 
         KeytabEntry entry = keytab.getEntries().get( 0 );
@@ -152,6 +154,8 @@ public class KeytabTest
         Keytab writer = Keytab.getInstance();
         writer.setEntries( entries );
         ByteBuffer buffer = writer.write();
+
+        System.out.println( Strings.dumpBytes( buffer.array() ) );
         assertEquals( "Expected file size.", 130, buffer.limit() );
     }