J. Duke | 319a3b9 | 2007-12-01 00:00:00 +0000 | [diff] [blame^] | 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. Sun designates this |
| 7 | * particular file as subject to the "Classpath" exception as provided |
| 8 | * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
| 21 | * CA 95054 USA or visit www.sun.com if you need additional information or |
| 22 | * have any 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.Hashtable; |
| 36 | import java.util.Vector; |
| 37 | import java.util.StringTokenizer; |
| 38 | import sun.security.krb5.*; |
| 39 | import sun.security.krb5.internal.*; |
| 40 | import sun.security.krb5.internal.util.KrbDataInputStream; |
| 41 | |
| 42 | /** |
| 43 | * This class extends KrbDataInputStream. It is used for parsing FCC-format |
| 44 | * data from file to memory. |
| 45 | * |
| 46 | * @author Yanni Zhang |
| 47 | * |
| 48 | */ |
| 49 | public class CCacheInputStream extends KrbDataInputStream implements FileCCacheConstants { |
| 50 | |
| 51 | /* |
| 52 | * FCC version 2 contains type information for principals. FCC |
| 53 | * version 1 does not. |
| 54 | * |
| 55 | * FCC version 3 contains keyblock encryption type information, and is |
| 56 | * architecture independent. Previous versions are not. |
| 57 | * |
| 58 | * The code will accept version 1, 2, and 3 ccaches, and depending |
| 59 | * what KRB5_FCC_DEFAULT_FVNO is set to, it will create version 1, 2, |
| 60 | * or 3 FCC caches. |
| 61 | * |
| 62 | * The default credentials cache should be type 3 for now (see |
| 63 | * init_ctx.c). |
| 64 | */ |
| 65 | /* V4 of the credentials cache format allows for header tags */ |
| 66 | |
| 67 | private static boolean DEBUG = Krb5.DEBUG; |
| 68 | |
| 69 | public CCacheInputStream(InputStream is){ |
| 70 | super(is); |
| 71 | } |
| 72 | |
| 73 | /* Read tag field introduced in KRB5_FCC_FVNO_4 */ |
| 74 | // this needs to be public for Kinit. |
| 75 | public Tag readTag() throws IOException { |
| 76 | char[] buf = new char[1024]; |
| 77 | byte[] bytes; |
| 78 | int len; |
| 79 | int tag = -1; |
| 80 | int taglen; |
| 81 | Integer time_offset = null; |
| 82 | Integer usec_offset = null; |
| 83 | |
| 84 | len = read(2); |
| 85 | if (len < 0) { |
| 86 | throw new IOException("stop."); |
| 87 | } |
| 88 | bytes = new byte[len + 2]; |
| 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 = new Integer(read(4)); |
| 98 | usec_offset = new Integer(read(4)); |
| 99 | break; |
| 100 | default: |
| 101 | } |
| 102 | len = len - (4 + taglen); |
| 103 | } |
| 104 | Tag result; |
| 105 | if (tag == -1) { |
| 106 | } |
| 107 | result = new Tag(len, tag, time_offset, usec_offset); |
| 108 | return result; |
| 109 | } |
| 110 | /* |
| 111 | * In file-based credential cache, the realm name is stored as part of |
| 112 | * principal name at the first place. |
| 113 | */ |
| 114 | // made public for KinitOptions to call directly |
| 115 | public PrincipalName readPrincipal(int version) throws IOException, RealmException { |
| 116 | int type, length, namelength, kret; |
| 117 | PrincipalName p; |
| 118 | String[] pname = null; |
| 119 | String realm; |
| 120 | /* Read principal type */ |
| 121 | if (version == KRB5_FCC_FVNO_1) { |
| 122 | type = KRB5_NT_UNKNOWN; |
| 123 | } else { |
| 124 | type = read(4); |
| 125 | } |
| 126 | length = read(4); |
| 127 | String[] result = new String[length + 1]; |
| 128 | /* |
| 129 | * DCE includes the principal's realm in the count; the new format |
| 130 | * does not. |
| 131 | */ |
| 132 | if (version == KRB5_FCC_FVNO_1) |
| 133 | length--; |
| 134 | for (int i = 0; i <= length; i++) { |
| 135 | namelength = read(4); |
| 136 | if (namelength > MAXNAMELENGTH) { |
| 137 | throw new IOException("Invalid name length in principal name."); |
| 138 | } |
| 139 | byte[] bytes = new byte[namelength]; |
| 140 | read(bytes, 0, namelength); |
| 141 | result[i] = new String(bytes); |
| 142 | } |
| 143 | if (isRealm(result[0])) { |
| 144 | realm = result[0]; |
| 145 | pname = new String[length]; |
| 146 | System.arraycopy(result, 1, pname, 0, length); |
| 147 | p = new PrincipalName(pname, type); |
| 148 | p.setRealm(realm); |
| 149 | } |
| 150 | else p = new PrincipalName(result, type); |
| 151 | return p; |
| 152 | } |
| 153 | |
| 154 | /* |
| 155 | * In practice, a realm is named by uppercasing the DNS domain name. we currently |
| 156 | * rely on this to determine if the string within the principal identifier is realm |
| 157 | * name. |
| 158 | * |
| 159 | */ |
| 160 | boolean isRealm(String str) { |
| 161 | try { |
| 162 | Realm r = new Realm(str); |
| 163 | } |
| 164 | catch (Exception e) { |
| 165 | return false; |
| 166 | } |
| 167 | StringTokenizer st = new StringTokenizer(str, "."); |
| 168 | String s; |
| 169 | while (st.hasMoreTokens()) { |
| 170 | s = st.nextToken(); |
| 171 | for (int i = 0; i < s.length(); i++) { |
| 172 | if (s.charAt(i) >= 141) { |
| 173 | return false; |
| 174 | } |
| 175 | } |
| 176 | } |
| 177 | return true; |
| 178 | } |
| 179 | |
| 180 | EncryptionKey readKey(int version) throws IOException { |
| 181 | int keyType, keyLen; |
| 182 | keyType = read(2); |
| 183 | if (version == KRB5_FCC_FVNO_3) |
| 184 | read(2); /* keytype recorded twice in fvno 3 */ |
| 185 | keyLen = read(4); |
| 186 | byte[] bytes = new byte[keyLen]; |
| 187 | for (int i = 0; i < keyLen; i++) { |
| 188 | bytes[i] = (byte)read(); |
| 189 | } |
| 190 | return new EncryptionKey(bytes, keyType, new Integer(version)); |
| 191 | } |
| 192 | |
| 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 | |
| 202 | boolean readskey() throws IOException { |
| 203 | if (read() == 0) { |
| 204 | return false; |
| 205 | } |
| 206 | else return true; |
| 207 | } |
| 208 | |
| 209 | HostAddress[] readAddr() throws IOException, KrbApErrException { |
| 210 | int numAddrs, addrType, addrLength; |
| 211 | numAddrs = read(4); |
| 212 | if (numAddrs > 0) { |
| 213 | HostAddress[] addrs = new HostAddress[numAddrs]; |
| 214 | for (int i = 0; i < numAddrs; i++) { |
| 215 | addrType = read(2); |
| 216 | addrLength = read(4); |
| 217 | if (!(addrLength == 4 || addrLength == 16)) { |
| 218 | System.out.println("Incorrect address format."); |
| 219 | return null; |
| 220 | } |
| 221 | byte[] result = new byte[addrLength]; |
| 222 | for (int j = 0; j < addrLength; j++) |
| 223 | result[j] = (byte)read(1); |
| 224 | addrs[i] = new HostAddress(addrType, result); |
| 225 | } |
| 226 | return addrs; |
| 227 | } |
| 228 | return null; |
| 229 | } |
| 230 | |
| 231 | AuthorizationDataEntry[] readAuth() throws IOException { |
| 232 | int num, adtype, adlength; |
| 233 | num = read(4); |
| 234 | if (num > 0) { |
| 235 | AuthorizationDataEntry[] auData = new AuthorizationDataEntry[num]; |
| 236 | byte[] data = null; |
| 237 | for (int i = 0; i < num; i++) { |
| 238 | adtype = read(2); |
| 239 | adlength = read(4); |
| 240 | data = new byte[adlength]; |
| 241 | for (int j = 0; j < adlength; j++) { |
| 242 | data[j] = (byte)read(); |
| 243 | } |
| 244 | auData[i] = new AuthorizationDataEntry(adtype, data); |
| 245 | } |
| 246 | return auData; |
| 247 | } |
| 248 | else return null; |
| 249 | } |
| 250 | |
| 251 | Ticket readData() throws IOException, RealmException, KrbApErrException, Asn1Exception { |
| 252 | int length; |
| 253 | length = read(4); |
| 254 | if (length > 0) { |
| 255 | byte[] bytes = new byte[length]; |
| 256 | read(bytes, 0, length); |
| 257 | Ticket ticket = new Ticket(bytes); |
| 258 | return ticket; |
| 259 | } |
| 260 | else return null; |
| 261 | } |
| 262 | |
| 263 | boolean[] readFlags() throws IOException { |
| 264 | boolean[] flags = new boolean[Krb5.TKT_OPTS_MAX+1]; |
| 265 | int ticketFlags; |
| 266 | ticketFlags = read(4); |
| 267 | if ((ticketFlags & 0x40000000) == TKT_FLG_FORWARDABLE) |
| 268 | flags[1] = true; |
| 269 | if ((ticketFlags & 0x20000000) == TKT_FLG_FORWARDED) |
| 270 | flags[2] = true; |
| 271 | if ((ticketFlags & 0x10000000) == TKT_FLG_PROXIABLE) |
| 272 | flags[3] = true; |
| 273 | if ((ticketFlags & 0x08000000) == TKT_FLG_PROXY) |
| 274 | flags[4] = true; |
| 275 | if ((ticketFlags & 0x04000000) == TKT_FLG_MAY_POSTDATE) |
| 276 | flags[5] = true; |
| 277 | if ((ticketFlags & 0x02000000) == TKT_FLG_POSTDATED) |
| 278 | flags[6] = true; |
| 279 | if ((ticketFlags & 0x01000000) == TKT_FLG_INVALID) |
| 280 | flags[7] = true; |
| 281 | if ((ticketFlags & 0x00800000) == TKT_FLG_RENEWABLE) |
| 282 | flags[8] = true; |
| 283 | if ((ticketFlags & 0x00400000) == TKT_FLG_INITIAL) |
| 284 | flags[9] = true; |
| 285 | if ((ticketFlags & 0x00200000) == TKT_FLG_PRE_AUTH) |
| 286 | flags[10] = true; |
| 287 | if ((ticketFlags & 0x00100000) == TKT_FLG_HW_AUTH) |
| 288 | flags[11] = true; |
| 289 | if (DEBUG) { |
| 290 | String msg = ">>> CCacheInputStream: readFlags() "; |
| 291 | if (flags[1] == true) { |
| 292 | msg += " FORWARDABLE;"; |
| 293 | } |
| 294 | if (flags[2] == true) { |
| 295 | msg += " FORWARDED;"; |
| 296 | } |
| 297 | if (flags[3] == true) { |
| 298 | msg += " PROXIABLE;"; |
| 299 | } |
| 300 | if (flags[4] == true) { |
| 301 | msg += " PROXY;"; |
| 302 | } |
| 303 | if (flags[5] == true) { |
| 304 | msg += " MAY_POSTDATE;"; |
| 305 | } |
| 306 | if (flags[6] == true) { |
| 307 | msg += " POSTDATED;"; |
| 308 | } |
| 309 | if (flags[7] == true) { |
| 310 | msg += " INVALID;"; |
| 311 | } |
| 312 | if (flags[8] == true) { |
| 313 | msg += " RENEWABLE;"; |
| 314 | } |
| 315 | |
| 316 | if (flags[9] == true) { |
| 317 | msg += " INITIAL;"; |
| 318 | } |
| 319 | if (flags[10] == true) { |
| 320 | msg += " PRE_AUTH;"; |
| 321 | } |
| 322 | if (flags[11] == true) { |
| 323 | msg += " HW_AUTH;"; |
| 324 | } |
| 325 | System.out.println(msg); |
| 326 | } |
| 327 | return flags; |
| 328 | } |
| 329 | Credentials readCred(int version) throws IOException,RealmException, KrbApErrException, Asn1Exception { |
| 330 | PrincipalName cpname = readPrincipal(version); |
| 331 | if (DEBUG) |
| 332 | System.out.println(">>>DEBUG <CCacheInputStream> client principal is " + cpname.toString()); |
| 333 | PrincipalName spname = readPrincipal(version); |
| 334 | if (DEBUG) |
| 335 | System.out.println(">>>DEBUG <CCacheInputStream> server principal is " + spname.toString()); |
| 336 | EncryptionKey key = readKey(version); |
| 337 | if (DEBUG) |
| 338 | System.out.println(">>>DEBUG <CCacheInputStream> key type: " + key.getEType()); |
| 339 | long times[] = readTimes(); |
| 340 | KerberosTime authtime = new KerberosTime(times[0]); |
| 341 | KerberosTime starttime = new KerberosTime(times[1]); |
| 342 | KerberosTime endtime = new KerberosTime(times[2]); |
| 343 | KerberosTime renewTill = new KerberosTime(times[3]); |
| 344 | |
| 345 | if (DEBUG) { |
| 346 | System.out.println(">>>DEBUG <CCacheInputStream> auth time: " + authtime.toDate().toString()); |
| 347 | System.out.println(">>>DEBUG <CCacheInputStream> start time: " + starttime.toDate().toString()); |
| 348 | System.out.println(">>>DEBUG <CCacheInputStream> end time: " + endtime.toDate().toString()); |
| 349 | System.out.println(">>>DEBUG <CCacheInputStream> renew_till time: " + renewTill.toDate().toString()); |
| 350 | } |
| 351 | boolean skey = readskey(); |
| 352 | boolean flags[] = readFlags(); |
| 353 | TicketFlags tFlags = new TicketFlags(flags); |
| 354 | HostAddress addr[] = readAddr(); |
| 355 | HostAddresses addrs = null; |
| 356 | if (addr != null) { |
| 357 | addrs = new HostAddresses(addr); |
| 358 | } |
| 359 | AuthorizationDataEntry[] auDataEntry = readAuth(); |
| 360 | AuthorizationData auData = null; |
| 361 | if (auData != null) { |
| 362 | auData = new AuthorizationData(auDataEntry); |
| 363 | } |
| 364 | Ticket ticket = readData(); |
| 365 | if (DEBUG) { |
| 366 | System.out.println(">>>DEBUG <CCacheInputStream>"); |
| 367 | if (ticket == null) { |
| 368 | System.out.println("///ticket is null"); |
| 369 | } |
| 370 | } |
| 371 | Ticket secTicket = readData(); |
| 372 | Credentials cred = new Credentials(cpname, spname, key, authtime, starttime, |
| 373 | endtime, renewTill, skey, tFlags, |
| 374 | addrs, auData, ticket, secTicket); |
| 375 | return cred; |
| 376 | } |
| 377 | } |