1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 3 * 4 * This code is free software; you can redistribute it and/or modify it 5 * under the terms of the GNU General Public License version 2 only, as 6 * published by the Free Software Foundation. Oracle designates this 7 * particular file as subject to the "Classpath" exception as provided 8 * by Oracle in the LICENSE file that accompanied this code. 9 * 10 * This code is distributed in the hope that it will be useful, but WITHOUT 11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 13 * version 2 for more details (a copy is included in the LICENSE file that 14 * accompanied this code). 15 * 16 * You should have received a copy of the GNU General Public License version 17 * 2 along with this work; if not, write to the Free Software Foundation, 18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 21 * or visit www.oracle.com if you need additional information or have any 22 * questions. 23 */ 24 25 /* 26 * 27 * (C) Copyright IBM Corp. 1999 All Rights Reserved. 28 * Copyright 1997 The Open Group Research Institute. All rights reserved. 29 */ 30 31 package sun.security.krb5.internal.ccache; 32 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.StringTokenizer; 38 39 import sun.security.krb5.*; 40 import sun.security.krb5.internal.*; 41 import sun.security.krb5.internal.util.KrbDataInputStream; 42 import sun.security.util.IOUtils; 43 44 /** 45 * This class extends KrbDataInputStream. It is used for parsing FCC-format 46 * data from file to memory. 47 * 48 * @author Yanni Zhang 49 * 50 */ 51 public class CCacheInputStream extends KrbDataInputStream implements FileCCacheConstants { 52 53 /* 54 * FCC version 2 contains type information for principals. FCC 55 * version 1 does not. 56 * 57 * FCC version 3 contains keyblock encryption type information, and is 58 * architecture independent. Previous versions are not. 59 * 60 * The code will accept version 1, 2, and 3 ccaches, and depending 61 * what KRB5_FCC_DEFAULT_FVNO is set to, it will create version 1, 2, 62 * or 3 FCC caches. 63 * 64 * The default credentials cache should be type 3 for now (see 65 * init_ctx.c). 66 */ 67 /* V4 of the credentials cache format allows for header tags */ 68 69 private static boolean DEBUG = Krb5.DEBUG; 70 CCacheInputStream(InputStream is)71 public CCacheInputStream(InputStream is){ 72 super(is); 73 } 74 75 /* Read tag field introduced in KRB5_FCC_FVNO_4 */ 76 // this needs to be public for Kinit. readTag()77 public Tag readTag() throws IOException { 78 char[] buf = new char[1024]; 79 int len; 80 int tag = -1; 81 int taglen; 82 Integer time_offset = null; 83 Integer usec_offset = null; 84 85 len = read(2); 86 if (len < 0) { 87 throw new IOException("stop."); 88 } 89 if (len > buf.length) { 90 throw new IOException("Invalid tag length."); 91 } 92 while (len > 0) { 93 tag = read(2); 94 taglen = read(2); 95 switch (tag) { 96 case FCC_TAG_DELTATIME: 97 time_offset = read(4); 98 usec_offset = read(4); 99 break; 100 default: 101 } 102 len = len - (4 + taglen); 103 } 104 return new Tag(len, tag, time_offset, usec_offset); 105 } 106 /* 107 * In file-based credential cache, the realm name is stored as part of 108 * principal name at the first place. 109 */ 110 // made public for KinitOptions to call directly readPrincipal(int version)111 public PrincipalName readPrincipal(int version) throws IOException, RealmException { 112 int type, length, namelength, kret; 113 String[] pname = null; 114 String realm; 115 /* Read principal type */ 116 if (version == KRB5_FCC_FVNO_1) { 117 type = KRB5_NT_UNKNOWN; 118 } else { 119 type = read(4); 120 } 121 length = readLength4(); 122 List<String> result = new ArrayList<String>(); 123 /* 124 * DCE includes the principal's realm in the count; the new format 125 * does not. 126 */ 127 if (version == KRB5_FCC_FVNO_1) 128 length--; 129 for (int i = 0; i <= length; i++) { 130 namelength = readLength4(); 131 byte[] bytes = IOUtils.readFully(this, namelength, true); 132 result.add(new String(bytes)); 133 } 134 if (result.isEmpty()) { 135 throw new IOException("No realm or principal"); 136 } 137 if (isRealm(result.get(0))) { 138 realm = result.remove(0); 139 if (result.isEmpty()) { 140 throw new IOException("No principal name components"); 141 } 142 return new PrincipalName( 143 type, 144 result.toArray(new String[result.size()]), 145 new Realm(realm)); 146 } 147 try { 148 return new PrincipalName( 149 type, 150 result.toArray(new String[result.size()]), 151 Realm.getDefault()); 152 } catch (RealmException re) { 153 return null; 154 } 155 } 156 157 /* 158 * In practice, a realm is named by uppercasing the DNS domain name. we currently 159 * rely on this to determine if the string within the principal identifier is realm 160 * name. 161 * 162 */ isRealm(String str)163 boolean isRealm(String str) { 164 try { 165 Realm r = new Realm(str); 166 } 167 catch (Exception e) { 168 return false; 169 } 170 StringTokenizer st = new StringTokenizer(str, "."); 171 String s; 172 while (st.hasMoreTokens()) { 173 s = st.nextToken(); 174 for (int i = 0; i < s.length(); i++) { 175 if (s.charAt(i) >= 141) { 176 return false; 177 } 178 } 179 } 180 return true; 181 } 182 readKey(int version)183 EncryptionKey readKey(int version) throws IOException { 184 int keyType, keyLen; 185 keyType = read(2); 186 if (version == KRB5_FCC_FVNO_3) 187 read(2); /* keytype recorded twice in fvno 3 */ 188 keyLen = readLength4(); 189 byte[] bytes = IOUtils.readFully(this, keyLen, true); 190 return new EncryptionKey(bytes, keyType, version); 191 } 192 readTimes()193 long[] readTimes() throws IOException { 194 long[] times = new long[4]; 195 times[0] = (long)read(4) * 1000; 196 times[1] = (long)read(4) * 1000; 197 times[2] = (long)read(4) * 1000; 198 times[3] = (long)read(4) * 1000; 199 return times; 200 } 201 readskey()202 boolean readskey() throws IOException { 203 if (read() == 0) { 204 return false; 205 } 206 else return true; 207 } 208 readAddr()209 HostAddress[] readAddr() throws IOException, KrbApErrException { 210 int numAddrs, addrType, addrLength; 211 numAddrs = readLength4(); 212 if (numAddrs > 0) { 213 List<HostAddress> addrs = new ArrayList<>(); 214 for (int i = 0; i < numAddrs; i++) { 215 addrType = read(2); 216 addrLength = readLength4(); 217 if (!(addrLength == 4 || addrLength == 16)) { 218 if (DEBUG) { 219 System.out.println("Incorrect address format."); 220 } 221 return null; 222 } 223 byte[] result = new byte[addrLength]; 224 for (int j = 0; j < addrLength; j++) 225 result[j] = (byte)read(1); 226 addrs.add(new HostAddress(addrType, result)); 227 } 228 return addrs.toArray(new HostAddress[addrs.size()]); 229 } 230 return null; 231 } 232 readAuth()233 AuthorizationDataEntry[] readAuth() throws IOException { 234 int num, adtype, adlength; 235 num = readLength4(); 236 if (num > 0) { 237 List<AuthorizationDataEntry> auData = new ArrayList<>(); 238 byte[] data = null; 239 for (int i = 0; i < num; i++) { 240 adtype = read(2); 241 adlength = readLength4(); 242 data = IOUtils.readFully(this, adlength, true); 243 auData.add(new AuthorizationDataEntry(adtype, data)); 244 } 245 return auData.toArray(new AuthorizationDataEntry[auData.size()]); 246 } 247 else return null; 248 } 249 readData()250 byte[] readData() throws IOException { 251 int length; 252 length = readLength4(); 253 if (length == 0) { 254 return null; 255 } else { 256 return IOUtils.readFully(this, length, true); 257 } 258 } 259 readFlags()260 boolean[] readFlags() throws IOException { 261 boolean[] flags = new boolean[Krb5.TKT_OPTS_MAX+1]; 262 int ticketFlags; 263 ticketFlags = read(4); 264 if ((ticketFlags & 0x40000000) == TKT_FLG_FORWARDABLE) 265 flags[1] = true; 266 if ((ticketFlags & 0x20000000) == TKT_FLG_FORWARDED) 267 flags[2] = true; 268 if ((ticketFlags & 0x10000000) == TKT_FLG_PROXIABLE) 269 flags[3] = true; 270 if ((ticketFlags & 0x08000000) == TKT_FLG_PROXY) 271 flags[4] = true; 272 if ((ticketFlags & 0x04000000) == TKT_FLG_MAY_POSTDATE) 273 flags[5] = true; 274 if ((ticketFlags & 0x02000000) == TKT_FLG_POSTDATED) 275 flags[6] = true; 276 if ((ticketFlags & 0x01000000) == TKT_FLG_INVALID) 277 flags[7] = true; 278 if ((ticketFlags & 0x00800000) == TKT_FLG_RENEWABLE) 279 flags[8] = true; 280 if ((ticketFlags & 0x00400000) == TKT_FLG_INITIAL) 281 flags[9] = true; 282 if ((ticketFlags & 0x00200000) == TKT_FLG_PRE_AUTH) 283 flags[10] = true; 284 if ((ticketFlags & 0x00100000) == TKT_FLG_HW_AUTH) 285 flags[11] = true; 286 if (DEBUG) { 287 String msg = ">>> CCacheInputStream: readFlags() "; 288 if (flags[1] == true) { 289 msg += " FORWARDABLE;"; 290 } 291 if (flags[2] == true) { 292 msg += " FORWARDED;"; 293 } 294 if (flags[3] == true) { 295 msg += " PROXIABLE;"; 296 } 297 if (flags[4] == true) { 298 msg += " PROXY;"; 299 } 300 if (flags[5] == true) { 301 msg += " MAY_POSTDATE;"; 302 } 303 if (flags[6] == true) { 304 msg += " POSTDATED;"; 305 } 306 if (flags[7] == true) { 307 msg += " INVALID;"; 308 } 309 if (flags[8] == true) { 310 msg += " RENEWABLE;"; 311 } 312 313 if (flags[9] == true) { 314 msg += " INITIAL;"; 315 } 316 if (flags[10] == true) { 317 msg += " PRE_AUTH;"; 318 } 319 if (flags[11] == true) { 320 msg += " HW_AUTH;"; 321 } 322 System.out.println(msg); 323 } 324 return flags; 325 } 326 327 /** 328 * Reads the next cred in stream. 329 * @return the next cred, null if ticket or second_ticket unparseable. 330 * 331 * Note: MIT krb5 1.8.1 might generate a config entry with server principal 332 * X-CACHECONF:/krb5_ccache_conf_data/fast_avail/krbtgt/REALM@REALM. The 333 * entry is used by KDC to inform the client that it support certain 334 * features. Its ticket is not a valid krb5 ticket and thus this method 335 * returns null. 336 */ readCred(int version)337 Credentials readCred(int version) throws IOException,RealmException, KrbApErrException, Asn1Exception { 338 PrincipalName cpname = null; 339 try { 340 cpname = readPrincipal(version); 341 } catch (Exception e) { 342 // Do not return here. All data for this cred should be fully 343 // consumed so that we can read the next one. 344 } 345 if (DEBUG) { 346 System.out.println(">>>DEBUG <CCacheInputStream> client principal is " + cpname); 347 } 348 PrincipalName spname = null; 349 try { 350 spname = readPrincipal(version); 351 } catch (Exception e) { 352 // same as above 353 } 354 if (DEBUG) { 355 System.out.println(">>>DEBUG <CCacheInputStream> server principal is " + spname); 356 } 357 EncryptionKey key = readKey(version); 358 if (DEBUG) { 359 System.out.println(">>>DEBUG <CCacheInputStream> key type: " + key.getEType()); 360 } 361 long[] times = readTimes(); 362 KerberosTime authtime = new KerberosTime(times[0]); 363 KerberosTime starttime = 364 (times[1]==0) ? null : new KerberosTime(times[1]); 365 KerberosTime endtime = new KerberosTime(times[2]); 366 KerberosTime renewTill = 367 (times[3]==0) ? null : new KerberosTime(times[3]); 368 369 if (DEBUG) { 370 System.out.println(">>>DEBUG <CCacheInputStream> auth time: " + authtime.toDate().toString()); 371 System.out.println(">>>DEBUG <CCacheInputStream> start time: " + 372 ((starttime==null)?"null":starttime.toDate().toString())); 373 System.out.println(">>>DEBUG <CCacheInputStream> end time: " + endtime.toDate().toString()); 374 System.out.println(">>>DEBUG <CCacheInputStream> renew_till time: " + 375 ((renewTill==null)?"null":renewTill.toDate().toString())); 376 } 377 boolean skey = readskey(); 378 boolean[] flags = readFlags(); 379 TicketFlags tFlags = new TicketFlags(flags); 380 HostAddress[] addr = readAddr(); 381 HostAddresses addrs = null; 382 if (addr != null) { 383 addrs = new HostAddresses(addr); 384 } 385 AuthorizationDataEntry[] auDataEntry = readAuth(); 386 AuthorizationData auData = null; 387 if (auDataEntry != null) { 388 auData = new AuthorizationData(auDataEntry); 389 } 390 byte[] ticketData = readData(); 391 byte[] ticketData2 = readData(); 392 393 // Skip this cred if either cpname or spname isn't created. 394 if (cpname == null || spname == null) { 395 return null; 396 } 397 398 try { 399 return new Credentials(cpname, spname, key, authtime, starttime, 400 endtime, renewTill, skey, tFlags, 401 addrs, auData, 402 ticketData != null ? new Ticket(ticketData) : null, 403 ticketData2 != null ? new Ticket(ticketData2) : null); 404 } catch (Exception e) { // If any of new Ticket(*) fails. 405 return null; 406 } 407 } 408 } 409