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.kerberos.credentials.cache;
21  
22  
23  import java.io.DataInputStream;
24  import java.io.IOException;
25  import java.io.InputStream;
26  import java.nio.charset.StandardCharsets;
27  import java.util.ArrayList;
28  import java.util.List;
29  
30  import org.apache.directory.shared.kerberos.KerberosTime;
31  import org.apache.directory.shared.kerberos.codec.KerberosDecoder;
32  import org.apache.directory.shared.kerberos.codec.types.AuthorizationType;
33  import org.apache.directory.shared.kerberos.codec.types.EncryptionType;
34  import org.apache.directory.shared.kerberos.codec.types.HostAddrType;
35  import org.apache.directory.shared.kerberos.components.AuthorizationData;
36  import org.apache.directory.shared.kerberos.components.AuthorizationDataEntry;
37  import org.apache.directory.shared.kerberos.components.EncryptionKey;
38  import org.apache.directory.shared.kerberos.components.HostAddress;
39  import org.apache.directory.shared.kerberos.components.HostAddresses;
40  import org.apache.directory.shared.kerberos.components.PrincipalName;
41  import org.apache.directory.shared.kerberos.flags.TicketFlags;
42  
43  
44  /**
45   * Reading credentials cache according to FCC format by reference the following
46   * https://www.gnu.org/software/shishi/manual/html_node/The-Credential-Cache-Binary-File-Format.html
47   * 
48   * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a>
49   */
50  public class CacheInputStream extends DataInputStream
51  {
52      public CacheInputStream( InputStream in )
53      {
54          super( in );
55      }
56  
57  
58      public void read( CredentialsCache cache ) throws IOException
59      {
60          int version;
61          List<Tag> tags;
62          PrincipalName principal;
63          Credentials cred;
64  
65          version = readVersion();
66          cache.setVersion( version );
67  
68          if ( version == CredentialsCacheConstants.FCC_FVNO_4 )
69          {
70              tags = readTag();
71          }
72          else
73          {
74              tags = null;
75          }
76          cache.setTags( tags );
77  
78          principal = readPrincipal( version );
79          cache.setPrimaryPrincipalName( principal );
80  
81          while ( available() > 0 )
82          {
83              cred = readCredentials( version );
84              if ( cred != null )
85              {
86                  cache.addCredentials( cred );
87              }
88          }
89      }
90  
91  
92      private int readVersion() throws IOException
93      {
94          return readShort();
95      }
96  
97  
98      private List<Tag> readTag() throws IOException
99      {
100         int len;
101         int tag;
102         int taglen;
103         int time;
104         int usec;
105 
106         len = readShort();
107         List<Tag> tags = new ArrayList<>();
108 
109         while ( len > 0 )
110         {
111             tag = readShort();
112             taglen = readShort();
113 
114             if ( tag == CredentialsCacheConstants.FCC_TAG_DELTATIME )
115             {
116                 time = readInt();
117                 usec = readInt();
118                 tags.add( new Tag( tag, time, usec ) );
119             }
120             else
121             {
122                 read( new byte[taglen], 0, taglen ); // ignore unknown tag
123             }
124 
125             len = len - ( 4 + taglen );
126         }
127 
128         return tags;
129     }
130 
131 
132     private PrincipalName readPrincipal( int version ) throws IOException
133     {
134         int type;
135         int length;
136         PrincipalName pname;
137 
138         if ( version == CredentialsCacheConstants.FCC_FVNO_1 )
139         {
140             type = CredentialsCacheConstants.NT_UNKNOWN;
141         }
142         else
143         {
144             type = readInt();
145         }
146 
147         length = readInt();
148 
149         if ( version == CredentialsCacheConstants.FCC_FVNO_1 )
150         {
151             length--;
152         }
153 
154         String realm = readCountedString();
155 
156         String[] result = new String[length];
157 
158         for ( int i = 0; i < length; i++ )
159         {
160             result[i] = readCountedString();
161         }
162 
163         pname = new PrincipalName( result, type );
164 
165         if ( isRealm( realm ) )
166         {
167             pname.setRealm( realm );
168         }
169 
170         return pname;
171     }
172 
173 
174     private String readCountedString() throws IOException
175     {
176         int namelength = readInt();
177         if ( namelength > CredentialsCacheConstants.MAXNAMELENGTH )
178         {
179             throw new IOException( "Invalid name length in principal name." );
180         }
181         byte[] bytes = new byte[namelength];
182         read( bytes, 0, bytes.length );
183 
184         return new String( bytes, StandardCharsets.UTF_8 );
185     }
186 
187 
188     /*
189      * Domain style realm names MUST look like domain names: they consist of
190      * components separated by periods (.) and they contain neither colons
191      * (:) nor slashes (/). When establishing a new realm name based on an 
192      * internet domain name it is recommended by convention that the characters 
193      * be converted to uppercase.
194      */
195     private static boolean isRealm( String str )
196     {
197         char chr;
198         for ( int i = 0; i < str.length(); i++ )
199         {
200             chr = str.charAt( i );
201             if ( chr != '.' && chr >= 'a' )
202             {
203                 return false;
204             }
205         }
206 
207         return true;
208     }
209 
210 
211     private EncryptionKey readKey( int version ) throws IOException
212     {
213         int keyType;
214         int keyLen;
215         keyType = readShort();
216 
217         if ( version == CredentialsCacheConstants.FCC_FVNO_3 )
218         {
219             readShort();
220         }
221 
222         // It's not correct with "uint16_t keylen", instead "uint32_t keylen" in keyblock 
223         keyLen = readInt();
224         byte[] bytes = new byte[keyLen];
225         read( bytes, 0, bytes.length );
226 
227         return new EncryptionKey( EncryptionType.getTypeByValue( keyType ), bytes );
228     }
229 
230 
231     private KerberosTime[] readKerberosTimes() throws IOException
232     {
233         long[] times = readTimes();
234         KerberosTimeberos/KerberosTime.html#KerberosTime">KerberosTime[] results = new KerberosTime[times.length];
235         KerberosTime ktime;
236 
237         for ( int i = 0; i < times.length; ++i )
238         {
239             ktime = times[i] == 0 ? null : new KerberosTime( times[i] );
240             results[i] = ktime;
241         }
242 
243         return results;
244     }
245 
246 
247     private long[] readTimes() throws IOException
248     {
249         long[] times = new long[4];
250         times[0] = ( long ) readInt() * 1000;
251         times[1] = ( long ) readInt() * 1000;
252         times[2] = ( long ) readInt() * 1000;
253         times[3] = ( long ) readInt() * 1000;
254 
255         return times;
256     }
257 
258 
259     private boolean readskey() throws IOException
260     {
261         return read() != 0;
262     }
263 
264 
265     private HostAddress[] readAddr() throws IOException
266     {
267         int numAddrs;
268         int addrType;
269         int addrLength;
270         numAddrs = readInt();
271 
272         if ( numAddrs > 0 )
273         {
274             HostAddresskerberos/components/HostAddress.html#HostAddress">HostAddress[] addrs = new HostAddress[numAddrs];
275 
276             for ( int i = 0; i < numAddrs; i++ )
277             {
278                 addrType = readShort();
279                 addrLength = readInt();
280 
281                 if ( !( addrLength == 4 || addrLength == 16 ) )
282                 {
283                     return null;
284                 }
285 
286                 byte[] result = new byte[addrLength];
287 
288                 for ( int j = 0; j < addrLength; j++ )
289                 {
290                     result[j] = readByte();
291                 }
292 
293                 addrs[i] = new HostAddress( HostAddrType.getTypeByOrdinal( addrType ), result );
294             }
295             return addrs;
296         }
297 
298         return null;
299     }
300 
301 
302     private AuthorizationDataEntry[] readAuth() throws IOException
303     {
304         int num;
305         int adtype;
306         int adlength;
307         num = readInt();
308 
309         if ( num > 0 )
310         {
311             AuthorizationDataEntryponents/AuthorizationDataEntry.html#AuthorizationDataEntry">AuthorizationDataEntry[] auData = new AuthorizationDataEntry[num];
312             byte[] data = null;
313 
314             for ( int i = 0; i < num; i++ )
315             {
316                 adtype = readShort();
317                 adlength = readInt();
318                 data = new byte[adlength];
319                 read( data, 0, data.length );
320                 auData[i] = new AuthorizationDataEntry( AuthorizationType.getTypeByValue( adtype ), data );
321             }
322 
323             return auData;
324         }
325 
326         return null;
327     }
328 
329 
330     private byte[] readData() throws IOException
331     {
332         int length;
333         length = readInt();
334         if ( length == 0 )
335         {
336             return null;
337         }
338         else
339         {
340             byte[] bytes = new byte[length];
341             read( bytes, 0, length );
342             return bytes;
343         }
344     }
345 
346 
347     private int readFlags() throws IOException
348     {
349         int ticketFlags;
350         ticketFlags = readInt();
351         return ticketFlags;
352     }
353 
354 
355     private Credentials readCredentials( int version ) throws IOException
356     {
357         PrincipalName cpname = readPrincipal( version );
358         PrincipalName spname = readPrincipal( version );
359 
360         EncryptionKey key = readKey( version );
361 
362         KerberosTime[] times = readKerberosTimes();
363         KerberosTime authtime = times[0];
364         KerberosTime starttime = times[1];
365         KerberosTime endtime = times[2];
366         KerberosTime renewTill = times[3];
367 
368         boolean skey = readskey();
369 
370         int flags = readFlags();
371         TicketFlags/kerberos/flags/TicketFlags.html#TicketFlags">TicketFlags tFlags = new TicketFlags( flags );
372         HostAddress addr[] = readAddr();
373         HostAddresses addrs = null;
374 
375         if ( addr != null )
376         {
377             addrs = new HostAddresses( addr );
378         }
379 
380         AuthorizationDataEntry[] auDataEntries = readAuth();
381         AuthorizationData auData = null;
382 
383         if ( auDataEntries != null )
384         {
385             auData = new AuthorizationData();
386 
387             for ( AuthorizationDataEntry ade : auDataEntries )
388             {
389                 auData.addEntry( ade );
390             }
391         }
392 
393         byte[] ticketData = readData();
394         byte[] ticketData2 = readData();
395 
396         if ( version != CredentialsCacheConstants.FCC_FVNO_1 &&
397             spname.getNameType().getValue() == CredentialsCacheConstants.NT_UNKNOWN )
398         {
399             // skip krb5_ccache_conf_data/fast_avail/krbtgt/REALM@REALM in MIT KRB5
400             return null;
401         }
402 
403         try
404         {
405             return new Credentials( cpname, spname, key, authtime, starttime,
406                 endtime, renewTill, skey, tFlags, addrs, auData,
407                 ticketData != null ? KerberosDecoder.decodeTicket( ticketData ) : null,
408                 ticketData2 != null ? KerberosDecoder.decodeTicket( ticketData2 ) : null );
409         }
410         catch ( Exception e )
411         {
412             return null;
413         }
414     }
415 }