View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *  
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *  
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License. 
18   *  
19   */
20  package org.apache.directory.server.kerberos.shared.keytab;
21  
22  
23  import java.io.UnsupportedEncodingException;
24  import java.util.ArrayList;
25  import java.util.List;
26  
27  import org.apache.directory.server.kerberos.shared.crypto.encryption.EncryptionType;
28  import org.apache.directory.server.kerberos.shared.messages.value.EncryptionKey;
29  import org.apache.directory.server.kerberos.shared.messages.value.KerberosTime;
30  import org.apache.mina.common.ByteBuffer;
31  
32  
33  /**
34   * Decode a {@link ByteBuffer} into keytab fields.
35   *
36   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
37   * @version $Rev$, $Date$
38   */
39  class KeytabDecoder
40  {
41      /**
42       * Read the keytab 16-bit file format version.  This
43       * keytab reader currently only supports version 5.2.
44       */
45      byte[] getKeytabVersion( ByteBuffer buffer )
46      {
47          byte[] version = new byte[2];
48          buffer.get( version );
49  
50          return version;
51      }
52  
53  
54      /**
55       * Read keytab entries until there is no remaining data
56       * in the buffer.
57       *
58       * @param buffer
59       * @return The keytab entries.
60       */
61      List<KeytabEntry> getKeytabEntries( ByteBuffer buffer )
62      {
63          List<KeytabEntry> entries = new ArrayList<KeytabEntry>();
64  
65          while ( buffer.remaining() > 0 )
66          {
67              int size = buffer.getInt();
68              byte[] entry = new byte[size];
69  
70              buffer.get( entry );
71              entries.add( getKeytabEntry( ByteBuffer.wrap( entry ) ) );
72          }
73  
74          return entries;
75      }
76  
77  
78      /**
79       * Reads off a "keytab entry," which consists of a principal name,
80       * principal type, key version number, and key material.
81       */
82      private KeytabEntry getKeytabEntry( ByteBuffer buffer )
83      {
84          String principalName = getPrincipalName( buffer );
85  
86          long principalType = buffer.getUnsignedInt();
87  
88          long time = buffer.getUnsignedInt();
89          KerberosTime timeStamp = new KerberosTime( time * 1000 );
90  
91          byte keyVersion = buffer.get();
92  
93          EncryptionKey key = getKeyBlock( buffer, keyVersion );
94  
95          return new KeytabEntry( principalName, principalType, timeStamp, keyVersion, key );
96      }
97  
98  
99      /**
100      * Reads off a principal name.
101      *
102      * @param buffer
103      * @return The principal name.
104      */
105     private String getPrincipalName( ByteBuffer buffer )
106     {
107         int count = buffer.getUnsignedShort();
108 
109         // decrement for v1
110         String realm = getCountedString( buffer );
111 
112         StringBuffer principalNameBuffer = new StringBuffer();
113 
114         for ( int ii = 0; ii < count; ii++ )
115         {
116             String nameComponent = getCountedString( buffer );
117 
118             principalNameBuffer.append( nameComponent );
119 
120             if ( ii < count - 1 )
121             {
122                 principalNameBuffer.append( "\\" );
123             }
124         }
125 
126         principalNameBuffer.append( "@" + realm );
127 
128         return principalNameBuffer.toString();
129     }
130 
131 
132     /**
133      * Read off a 16-bit encryption type and symmetric key material.
134      */
135     private EncryptionKey getKeyBlock( ByteBuffer buffer, int keyVersion )
136     {
137         int type = buffer.getUnsignedShort();
138         byte[] keyblock = getCountedBytes( buffer );
139 
140         EncryptionType encryptionType = EncryptionType.getTypeByOrdinal( type );
141         EncryptionKey key = new EncryptionKey( encryptionType, keyblock );
142 
143         return key;
144     }
145 
146 
147     /**
148      * Use a prefixed 16-bit length to read off a String.  Realm and name
149      * components are ASCII encoded text with no zero terminator.
150      */
151     private String getCountedString( ByteBuffer buffer )
152     {
153         int length = buffer.getUnsignedShort();
154         byte[] data = new byte[length];
155         buffer.get( data );
156 
157         try
158         {
159             return new String( data, "ASCII" );
160         }
161         catch ( UnsupportedEncodingException uee )
162         {
163             // Should never happen for ASCII
164             return "";
165         }
166     }
167 
168 
169     /**
170      * Use a prefixed 16-bit length to read off raw bytes.
171      */
172     private byte[] getCountedBytes( ByteBuffer buffer )
173     {
174         int length = buffer.getUnsignedShort();
175         byte[] data = new byte[length];
176         buffer.get( data );
177 
178         return data;
179     }
180 }