/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

/*
 *
 *  (C) Copyright IBM Corp. 1999 All Rights Reserved.
 *  Copyright 1997 The Open Group Research Institute.  All rights reserved.
 */

package sun.security.krb5.internal.ccache;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

import sun.security.krb5.*;
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.util.KrbDataInputStream;
import sun.security.util.IOUtils;

This class extends KrbDataInputStream. It is used for parsing FCC-format data from file to memory.
Author:Yanni Zhang
/** * This class extends KrbDataInputStream. It is used for parsing FCC-format * data from file to memory. * * @author Yanni Zhang * */
public class CCacheInputStream extends KrbDataInputStream implements FileCCacheConstants { /* * FCC version 2 contains type information for principals. FCC * version 1 does not. * * FCC version 3 contains keyblock encryption type information, and is * architecture independent. Previous versions are not. * * The code will accept version 1, 2, and 3 ccaches, and depending * what KRB5_FCC_DEFAULT_FVNO is set to, it will create version 1, 2, * or 3 FCC caches. * * The default credentials cache should be type 3 for now (see * init_ctx.c). */ /* V4 of the credentials cache format allows for header tags */ private static boolean DEBUG = Krb5.DEBUG; public CCacheInputStream(InputStream is){ super(is); } /* Read tag field introduced in KRB5_FCC_FVNO_4 */ // this needs to be public for Kinit. public Tag readTag() throws IOException { char[] buf = new char[1024]; int len; int tag = -1; int taglen; Integer time_offset = null; Integer usec_offset = null; len = read(2); if (len < 0) { throw new IOException("stop."); } if (len > buf.length) { throw new IOException("Invalid tag length."); } while (len > 0) { tag = read(2); taglen = read(2); switch (tag) { case FCC_TAG_DELTATIME: time_offset = read(4); usec_offset = read(4); break; default: } len = len - (4 + taglen); } return new Tag(len, tag, time_offset, usec_offset); } /* * In file-based credential cache, the realm name is stored as part of * principal name at the first place. */ // made public for KinitOptions to call directly public PrincipalName readPrincipal(int version) throws IOException, RealmException { int type, length, namelength, kret; String[] pname = null; String realm; /* Read principal type */ if (version == KRB5_FCC_FVNO_1) { type = KRB5_NT_UNKNOWN; } else { type = read(4); } length = readLength4(); List<String> result = new ArrayList<String>(); /* * DCE includes the principal's realm in the count; the new format * does not. */ if (version == KRB5_FCC_FVNO_1) length--; for (int i = 0; i <= length; i++) { namelength = readLength4(); byte[] bytes = IOUtils.readFully(this, namelength, true); result.add(new String(bytes)); } if (result.isEmpty()) { throw new IOException("No realm or principal"); } if (isRealm(result.get(0))) { realm = result.remove(0); if (result.isEmpty()) { throw new IOException("No principal name components"); } return new PrincipalName( type, result.toArray(new String[result.size()]), new Realm(realm)); } try { return new PrincipalName( type, result.toArray(new String[result.size()]), Realm.getDefault()); } catch (RealmException re) { return null; } } /* * In practice, a realm is named by uppercasing the DNS domain name. we currently * rely on this to determine if the string within the principal identifier is realm * name. * */ boolean isRealm(String str) { try { Realm r = new Realm(str); } catch (Exception e) { return false; } StringTokenizer st = new StringTokenizer(str, "."); String s; while (st.hasMoreTokens()) { s = st.nextToken(); for (int i = 0; i < s.length(); i++) { if (s.charAt(i) >= 141) { return false; } } } return true; } EncryptionKey readKey(int version) throws IOException { int keyType, keyLen; keyType = read(2); if (version == KRB5_FCC_FVNO_3) read(2); /* keytype recorded twice in fvno 3 */ keyLen = readLength4(); byte[] bytes = IOUtils.readFully(this, keyLen, true); return new EncryptionKey(bytes, keyType, version); } long[] readTimes() throws IOException { long[] times = new long[4]; times[0] = (long)read(4) * 1000; times[1] = (long)read(4) * 1000; times[2] = (long)read(4) * 1000; times[3] = (long)read(4) * 1000; return times; } boolean readskey() throws IOException { if (read() == 0) { return false; } else return true; } HostAddress[] readAddr() throws IOException, KrbApErrException { int numAddrs, addrType, addrLength; numAddrs = readLength4(); if (numAddrs > 0) { List<HostAddress> addrs = new ArrayList<>(); for (int i = 0; i < numAddrs; i++) { addrType = read(2); addrLength = readLength4(); if (!(addrLength == 4 || addrLength == 16)) { if (DEBUG) { System.out.println("Incorrect address format."); } return null; } byte[] result = new byte[addrLength]; for (int j = 0; j < addrLength; j++) result[j] = (byte)read(1); addrs.add(new HostAddress(addrType, result)); } return addrs.toArray(new HostAddress[addrs.size()]); } return null; } AuthorizationDataEntry[] readAuth() throws IOException { int num, adtype, adlength; num = readLength4(); if (num > 0) { List<AuthorizationDataEntry> auData = new ArrayList<>(); byte[] data = null; for (int i = 0; i < num; i++) { adtype = read(2); adlength = readLength4(); data = IOUtils.readFully(this, adlength, true); auData.add(new AuthorizationDataEntry(adtype, data)); } return auData.toArray(new AuthorizationDataEntry[auData.size()]); } else return null; } byte[] readData() throws IOException { int length; length = readLength4(); if (length == 0) { return null; } else { return IOUtils.readFully(this, length, true); } } boolean[] readFlags() throws IOException { boolean[] flags = new boolean[Krb5.TKT_OPTS_MAX+1]; int ticketFlags; ticketFlags = read(4); if ((ticketFlags & 0x40000000) == TKT_FLG_FORWARDABLE) flags[1] = true; if ((ticketFlags & 0x20000000) == TKT_FLG_FORWARDED) flags[2] = true; if ((ticketFlags & 0x10000000) == TKT_FLG_PROXIABLE) flags[3] = true; if ((ticketFlags & 0x08000000) == TKT_FLG_PROXY) flags[4] = true; if ((ticketFlags & 0x04000000) == TKT_FLG_MAY_POSTDATE) flags[5] = true; if ((ticketFlags & 0x02000000) == TKT_FLG_POSTDATED) flags[6] = true; if ((ticketFlags & 0x01000000) == TKT_FLG_INVALID) flags[7] = true; if ((ticketFlags & 0x00800000) == TKT_FLG_RENEWABLE) flags[8] = true; if ((ticketFlags & 0x00400000) == TKT_FLG_INITIAL) flags[9] = true; if ((ticketFlags & 0x00200000) == TKT_FLG_PRE_AUTH) flags[10] = true; if ((ticketFlags & 0x00100000) == TKT_FLG_HW_AUTH) flags[11] = true; if (DEBUG) { String msg = ">>> CCacheInputStream: readFlags() "; if (flags[1] == true) { msg += " FORWARDABLE;"; } if (flags[2] == true) { msg += " FORWARDED;"; } if (flags[3] == true) { msg += " PROXIABLE;"; } if (flags[4] == true) { msg += " PROXY;"; } if (flags[5] == true) { msg += " MAY_POSTDATE;"; } if (flags[6] == true) { msg += " POSTDATED;"; } if (flags[7] == true) { msg += " INVALID;"; } if (flags[8] == true) { msg += " RENEWABLE;"; } if (flags[9] == true) { msg += " INITIAL;"; } if (flags[10] == true) { msg += " PRE_AUTH;"; } if (flags[11] == true) { msg += " HW_AUTH;"; } System.out.println(msg); } return flags; }
Reads the next cred in stream.
Returns:the next cred, null if ticket or second_ticket unparseable. Note: MIT krb5 1.8.1 might generate a config entry with server principal X-CACHECONF:/krb5_ccache_conf_data/fast_avail/krbtgt/REALM@REALM. The entry is used by KDC to inform the client that it support certain features. Its ticket is not a valid krb5 ticket and thus this method returns null.
/** * Reads the next cred in stream. * @return the next cred, null if ticket or second_ticket unparseable. * * Note: MIT krb5 1.8.1 might generate a config entry with server principal * X-CACHECONF:/krb5_ccache_conf_data/fast_avail/krbtgt/REALM@REALM. The * entry is used by KDC to inform the client that it support certain * features. Its ticket is not a valid krb5 ticket and thus this method * returns null. */
Credentials readCred(int version) throws IOException,RealmException, KrbApErrException, Asn1Exception { PrincipalName cpname = null; try { cpname = readPrincipal(version); } catch (Exception e) { // Do not return here. All data for this cred should be fully // consumed so that we can read the next one. } if (DEBUG) { System.out.println(">>>DEBUG <CCacheInputStream> client principal is " + cpname); } PrincipalName spname = null; try { spname = readPrincipal(version); } catch (Exception e) { // same as above } if (DEBUG) { System.out.println(">>>DEBUG <CCacheInputStream> server principal is " + spname); } EncryptionKey key = readKey(version); if (DEBUG) { System.out.println(">>>DEBUG <CCacheInputStream> key type: " + key.getEType()); } long[] times = readTimes(); KerberosTime authtime = new KerberosTime(times[0]); KerberosTime starttime = (times[1]==0) ? null : new KerberosTime(times[1]); KerberosTime endtime = new KerberosTime(times[2]); KerberosTime renewTill = (times[3]==0) ? null : new KerberosTime(times[3]); if (DEBUG) { System.out.println(">>>DEBUG <CCacheInputStream> auth time: " + authtime.toDate().toString()); System.out.println(">>>DEBUG <CCacheInputStream> start time: " + ((starttime==null)?"null":starttime.toDate().toString())); System.out.println(">>>DEBUG <CCacheInputStream> end time: " + endtime.toDate().toString()); System.out.println(">>>DEBUG <CCacheInputStream> renew_till time: " + ((renewTill==null)?"null":renewTill.toDate().toString())); } boolean skey = readskey(); boolean[] flags = readFlags(); TicketFlags tFlags = new TicketFlags(flags); HostAddress[] addr = readAddr(); HostAddresses addrs = null; if (addr != null) { addrs = new HostAddresses(addr); } AuthorizationDataEntry[] auDataEntry = readAuth(); AuthorizationData auData = null; if (auDataEntry != null) { auData = new AuthorizationData(auDataEntry); } byte[] ticketData = readData(); byte[] ticketData2 = readData(); // Skip this cred if either cpname or spname isn't created. if (cpname == null || spname == null) { return null; } try { return new Credentials(cpname, spname, key, authtime, starttime, endtime, renewTill, skey, tFlags, addrs, auData, ticketData != null ? new Ticket(ticketData) : null, ticketData2 != null ? new Ticket(ticketData2) : null); } catch (Exception e) { // If any of new Ticket(*) fails. return null; } } }