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.File;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.io.OutputStream;
27  import java.nio.ByteBuffer;
28  import java.nio.channels.Channels;
29  import java.nio.channels.WritableByteChannel;
30  import java.nio.file.Files;
31  import java.util.ArrayList;
32  import java.util.Collections;
33  import java.util.List;
34  
35  import org.apache.directory.server.i18n.I18n;
36  
37  
38  /**
39   * Keytab file. The format is the following :
40   * <pre>
41   * { 
42   *   version : 2 bytes (0x05 0x02)
43   *   keytabEntry*
44   * }
45   *
46   * keytab_entry 
47   * {
48   *     size : int
49   *     numComponents :  short
50   *     realm : countedOctetString
51   *     components[numComponents] : countedOctetString
52   *     nameType : int
53   *     timestamp : int
54   *     vno8 : byte
55   *     key : keyBlock
56   *     vno : int // only present if &gt;= 4 bytes left in entry
57   * };
58   *
59   * keyblock 
60   * {
61   *     type : int
62   *     data : countedOctetString
63   * }
64   *
65   * countedOctetString 
66   * {
67   *     length : short
68   *     data[length] : bytes
69   * }
70   * </pre>
71   *
72   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
73   */
74  public class Keytab
75  {
76      /**
77       * Byte array constant for keytab file format 5.1.
78       */
79      public static final byte[] VERSION_0X501_BYTES = new byte[]
80          { ( byte ) 0x05, ( byte ) 0x01 };
81  
82      // Format 0x0501
83      public static final short VERSION_0X501 = 0x0501;
84  
85      /**
86       * Byte array constant for keytab file format 5.2.
87       */
88      public static final byte[] VERSION_0X502_BYTES = new byte[]
89          { ( byte ) 0x05, ( byte ) 0x02 };
90  
91      // Format 0x0502
92      public static final short VERSION_0X502 = 0x0502;
93  
94      private byte[] keytabVersion = VERSION_0X502_BYTES;
95      private List<KeytabEntry> entries = new ArrayList<>();
96  
97  
98      /**
99       * Read a keytab file.
100      *
101      * @param file
102      * @return The keytab.
103      * @throws IOException
104      */
105     public static Keytab read( File file ) throws IOException
106     {
107         ByteBuffer buffer = ByteBuffer.wrap( getBytesFromFile( file ) );
108         return readKeytab( buffer );
109     }
110 
111 
112     /**
113      * Returns a new instance of a keytab with the version
114      * defaulted to 5.2.
115      *
116      * @return The keytab.
117      */
118     public static Keytab getInstance()
119     {
120         return new Keytab();
121     }
122 
123 
124     /**
125      * Write the keytab to a {@link File}.
126      *
127      * @param file
128      * @throws IOException
129      */
130     public void write( File file ) throws IOException
131     {
132         KeytabEncoderr/kerberos/shared/keytab/KeytabEncoder.html#KeytabEncoder">KeytabEncoder writer = new KeytabEncoder();
133         ByteBuffer buffer = writer.write( keytabVersion, entries );
134         writeFile( buffer, file );
135     }
136 
137 
138     /**
139      * @param entries The entries to set.
140      */
141     public void setEntries( List<KeytabEntry> entries )
142     {
143         this.entries = entries;
144     }
145 
146 
147     /**
148      * @param keytabVersion The keytabVersion to set.
149      */
150     public void setKeytabVersion( byte[] keytabVersion )
151     {
152         this.keytabVersion = keytabVersion;
153     }
154 
155 
156     /**
157      * @return The entries.
158      */
159     public List<KeytabEntry> getEntries()
160     {
161         return Collections.unmodifiableList( entries );
162     }
163 
164 
165     /**
166      * @return The keytabVersion.
167      */
168     public byte[] getKeytabVersion()
169     {
170         return keytabVersion;
171     }
172 
173 
174     /**
175      * Read bytes into a keytab.
176      *
177      * @param bytes
178      * @return The keytab.
179      */
180     static Keytab read( byte[] bytes ) throws IOException
181     {
182         ByteBuffer buffer = ByteBuffer.wrap( bytes );
183         return readKeytab( buffer );
184     }
185 
186 
187     /**
188      * Write the keytab to a {@link ByteBuffer}.
189      * @return The buffer.
190      */
191     ByteBuffer write()
192     {
193         KeytabEncoderr/kerberos/shared/keytab/KeytabEncoder.html#KeytabEncoder">KeytabEncoder writer = new KeytabEncoder();
194         return writer.write( keytabVersion, entries );
195     }
196 
197 
198     /**
199      * Read the contents of the buffer into a keytab.
200      *
201      * @param buffer
202      * @return The keytab.
203      */
204     private static Keytab readKeytab( ByteBuffer buffer ) throws IOException
205     {
206         KeytabDecoderr/kerberos/shared/keytab/KeytabDecoder.html#KeytabDecoder">KeytabDecoder reader = new KeytabDecoder();
207         byte[] keytabVersion = reader.getKeytabVersion( buffer );
208         List<KeytabEntry> entries = reader.getKeytabEntries( buffer );
209 
210         Keytaby/server/kerberos/shared/keytab/Keytab.html#Keytab">Keytab keytab = new Keytab();
211 
212         keytab.setKeytabVersion( keytabVersion );
213         keytab.setEntries( entries );
214 
215         return keytab;
216     }
217 
218 
219     /**
220      * Returns the contents of the {@link File} in a byte array.
221      *
222      * @param file
223      * @return The byte array of the file contents.
224      * @throws IOException
225      */
226     protected static byte[] getBytesFromFile( File file ) throws IOException
227     {
228         try ( InputStream is = Files.newInputStream( file.toPath() ) )
229         {
230 
231             long length = file.length();
232 
233             // Check to ensure that file is not larger than Integer.MAX_VALUE.
234             if ( length > Integer.MAX_VALUE )
235             {
236                 throw new IOException( I18n.err( I18n.ERR_618, file.getName() ) );
237             }
238 
239             // Create the byte array to hold the data.
240             byte[] bytes = new byte[( int ) length];
241 
242             // Read in the bytes
243             int offset = 0;
244             int numRead = 0;
245             while ( offset < bytes.length && ( numRead = is.read( bytes, offset, bytes.length - offset ) ) >= 0 )
246             {
247                 offset += numRead;
248             }
249 
250             // Ensure all the bytes have been read in.
251             if ( offset < bytes.length )
252             {
253                 throw new IOException( I18n.err( I18n.ERR_619, file.getName() ) );
254             }
255 
256             return bytes;
257         }
258     }
259 
260 
261     /**
262      * Write the contents of the {@link ByteBuffer} to a {@link File}.
263      *
264      * @param buffer
265      * @param file
266      * @throws IOException
267      */
268     protected void writeFile( ByteBuffer buffer, File file ) throws IOException
269     {
270         // Set append false to replace existing.
271         OutputStream out = Files.newOutputStream( file.toPath() );
272 
273         try ( WritableByteChannel channel = Channels.newChannel( out ) )
274         {
275             // Write the bytes between the position and limit.
276             channel.write( buffer );
277         }
278         finally
279         {
280             out.close();
281         }
282     }
283 }