001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.directory.kerberos.credentials.cache; 021 022 023import java.io.DataInputStream; 024import java.io.IOException; 025import java.io.InputStream; 026import java.nio.charset.StandardCharsets; 027import java.util.ArrayList; 028import java.util.List; 029 030import org.apache.directory.shared.kerberos.KerberosTime; 031import org.apache.directory.shared.kerberos.codec.KerberosDecoder; 032import org.apache.directory.shared.kerberos.codec.types.AuthorizationType; 033import org.apache.directory.shared.kerberos.codec.types.EncryptionType; 034import org.apache.directory.shared.kerberos.codec.types.HostAddrType; 035import org.apache.directory.shared.kerberos.components.AuthorizationData; 036import org.apache.directory.shared.kerberos.components.AuthorizationDataEntry; 037import org.apache.directory.shared.kerberos.components.EncryptionKey; 038import org.apache.directory.shared.kerberos.components.HostAddress; 039import org.apache.directory.shared.kerberos.components.HostAddresses; 040import org.apache.directory.shared.kerberos.components.PrincipalName; 041import org.apache.directory.shared.kerberos.flags.TicketFlags; 042 043 044/** 045 * Reading credentials cache according to FCC format by reference the following 046 * https://www.gnu.org/software/shishi/manual/html_node/The-Credential-Cache-Binary-File-Format.html 047 * 048 * @author <a href="mailto:dev@directory.apache.org">Apache Directory Project</a> 049 */ 050public class CacheInputStream extends DataInputStream 051{ 052 public CacheInputStream( InputStream in ) 053 { 054 super( in ); 055 } 056 057 058 public void read( CredentialsCache cache ) throws IOException 059 { 060 int version; 061 List<Tag> tags; 062 PrincipalName principal; 063 Credentials cred; 064 065 version = readVersion(); 066 cache.setVersion( version ); 067 068 if ( version == CredentialsCacheConstants.FCC_FVNO_4 ) 069 { 070 tags = readTag(); 071 } 072 else 073 { 074 tags = null; 075 } 076 cache.setTags( tags ); 077 078 principal = readPrincipal( version ); 079 cache.setPrimaryPrincipalName( principal ); 080 081 while ( available() > 0 ) 082 { 083 cred = readCredentials( version ); 084 if ( cred != null ) 085 { 086 cache.addCredentials( cred ); 087 } 088 } 089 } 090 091 092 private int readVersion() throws IOException 093 { 094 return readShort(); 095 } 096 097 098 private List<Tag> readTag() throws IOException 099 { 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 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 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 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 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}