|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
/* |
|
* |
|
* (C) Copyright IBM Corp. 1999 All Rights Reserved. |
|
* Copyright 1997 The Open Group Research Institute. All rights reserved. |
|
*/ |
|
|
|
package sun.security.krb5.internal.ktab; |
|
|
|
import sun.security.krb5.*; |
|
import sun.security.krb5.internal.*; |
|
import sun.security.krb5.internal.crypto.*; |
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.io.IOException; |
|
import java.io.FileInputStream; |
|
import java.io.FileOutputStream; |
|
import java.io.File; |
|
import java.io.FileNotFoundException; |
|
import java.util.Comparator; |
|
import java.util.HashMap; |
|
import java.util.Map; |
|
import java.util.StringTokenizer; |
|
import java.util.Vector; |
|
import sun.security.jgss.krb5.ServiceCreds; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public class KeyTab implements KeyTabConstants { |
|
|
|
private static final boolean DEBUG = Krb5.DEBUG; |
|
private static String defaultTabName = null; |
|
|
|
// Attention: Currently there is no way to remove a keytab from this map, |
|
|
|
private static Map<String,KeyTab> map = new HashMap<>(); |
|
|
|
|
|
private boolean isMissing = false; |
|
|
|
|
|
private boolean isValid = true; |
|
|
|
private final String tabName; |
|
private long lastModified; |
|
private int kt_vno = KRB5_KT_VNO; |
|
|
|
private Vector<KeyTabEntry> entries = new Vector<>(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private KeyTab(String filename) { |
|
tabName = filename; |
|
try { |
|
lastModified = new File(tabName).lastModified(); |
|
try (KeyTabInputStream kis = |
|
new KeyTabInputStream(new FileInputStream(filename))) { |
|
load(kis); |
|
} |
|
} catch (FileNotFoundException e) { |
|
entries.clear(); |
|
isMissing = true; |
|
} catch (Exception ioe) { |
|
entries.clear(); |
|
isValid = false; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private synchronized static KeyTab getInstance0(String s) { |
|
long lm = new File(s).lastModified(); |
|
KeyTab old = map.get(s); |
|
if (old != null && old.isValid() && old.lastModified == lm) { |
|
return old; |
|
} |
|
KeyTab ktab = new KeyTab(s); |
|
if (ktab.isValid()) { |
|
map.put(s, ktab); |
|
return ktab; |
|
} else if (old != null) { |
|
return old; |
|
} else { |
|
return ktab; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static KeyTab getInstance(String s) { |
|
if (s == null) { |
|
return getInstance(); |
|
} else { |
|
return getInstance0(normalize(s)); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static KeyTab getInstance(File file) { |
|
if (file == null) { |
|
return getInstance(); |
|
} else { |
|
return getInstance0(file.getPath()); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public static KeyTab getInstance() { |
|
return getInstance(getDefaultTabName()); |
|
} |
|
|
|
public boolean isMissing() { |
|
return isMissing; |
|
} |
|
|
|
public boolean isValid() { |
|
return isValid; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private static String getDefaultTabName() { |
|
if (defaultTabName != null) { |
|
return defaultTabName; |
|
} else { |
|
String kname = null; |
|
try { |
|
String keytab_names = Config.getInstance().get |
|
("libdefaults", "default_keytab_name"); |
|
if (keytab_names != null) { |
|
StringTokenizer st = new StringTokenizer(keytab_names, " "); |
|
while (st.hasMoreTokens()) { |
|
kname = normalize(st.nextToken()); |
|
if (new File(kname).exists()) { |
|
break; |
|
} |
|
} |
|
} |
|
} catch (KrbException e) { |
|
kname = null; |
|
} |
|
|
|
if (kname == null) { |
|
String user_home = |
|
java.security.AccessController.doPrivileged( |
|
new sun.security.action.GetPropertyAction("user.home")); |
|
|
|
if (user_home == null) { |
|
user_home = |
|
java.security.AccessController.doPrivileged( |
|
new sun.security.action.GetPropertyAction("user.dir")); |
|
} |
|
|
|
kname = user_home + File.separator + "krb5.keytab"; |
|
} |
|
defaultTabName = kname; |
|
return kname; |
|
} |
|
} |
|
|
|
/** |
|
* Normalizes some common keytab name formats into the bare file name. |
|
* For example, FILE:/etc/krb5.keytab to /etc/krb5.keytab |
|
* @param name never null |
|
* @return never null |
|
*/ |
|
|
|
public static String normalize(String name) { |
|
String kname; |
|
if ((name.length() >= 5) && |
|
(name.substring(0, 5).equalsIgnoreCase("FILE:"))) { |
|
kname = name.substring(5); |
|
} else if ((name.length() >= 9) && |
|
(name.substring(0, 9).equalsIgnoreCase("ANY:FILE:"))) { |
|
|
|
kname = name.substring(9); |
|
} else if ((name.length() >= 7) && |
|
(name.substring(0, 7).equalsIgnoreCase("SRVTAB:"))) { |
|
|
|
kname = name.substring(7); |
|
} else |
|
kname = name; |
|
return kname; |
|
} |
|
|
|
private void load(KeyTabInputStream kis) |
|
throws IOException, RealmException { |
|
|
|
entries.clear(); |
|
kt_vno = kis.readVersion(); |
|
if (kt_vno == KRB5_KT_VNO_1) { |
|
kis.setNativeByteOrder(); |
|
} |
|
int entryLength = 0; |
|
KeyTabEntry entry; |
|
while (kis.available() > 0) { |
|
entryLength = kis.readEntryLength(); |
|
entry = kis.readEntry(entryLength, kt_vno); |
|
if (DEBUG) { |
|
System.out.println(">>> KeyTab: load() entry length: " + |
|
entryLength + "; type: " + |
|
(entry != null? entry.keyType : 0)); |
|
} |
|
if (entry != null) |
|
entries.addElement(entry); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public PrincipalName getOneName() { |
|
int size = entries.size(); |
|
return size > 0 ? entries.elementAt(size-1).service : null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public EncryptionKey[] readServiceKeys(PrincipalName service) { |
|
KeyTabEntry entry; |
|
EncryptionKey key; |
|
int size = entries.size(); |
|
ArrayList<EncryptionKey> keys = new ArrayList<>(size); |
|
if (DEBUG) { |
|
System.out.println("Looking for keys for: " + service); |
|
} |
|
for (int i = size-1; i >= 0; i--) { |
|
entry = entries.elementAt(i); |
|
if (entry.service.match(service)) { |
|
if (EType.isSupported(entry.keyType)) { |
|
key = new EncryptionKey(entry.keyblock, |
|
entry.keyType, |
|
new Integer(entry.keyVersion)); |
|
keys.add(key); |
|
if (DEBUG) { |
|
System.out.println("Added key: " + entry.keyType + |
|
"version: " + entry.keyVersion); |
|
} |
|
} else if (DEBUG) { |
|
System.out.println("Found unsupported keytype (" + |
|
entry.keyType + ") for " + service); |
|
} |
|
} |
|
} |
|
size = keys.size(); |
|
EncryptionKey[] retVal = keys.toArray(new EncryptionKey[size]); |
|
|
|
// Sort the keys by kvno. Sometimes we must choose a single key (say, |
|
// generate encrypted timestamp in AS-REQ). A key with a higher KVNO |
|
|
|
Arrays.sort(retVal, new Comparator<EncryptionKey>() { |
|
@Override |
|
public int compare(EncryptionKey o1, EncryptionKey o2) { |
|
return o2.getKeyVersionNumber().intValue() |
|
- o1.getKeyVersionNumber().intValue(); |
|
} |
|
}); |
|
|
|
return retVal; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean findServiceEntry(PrincipalName service) { |
|
KeyTabEntry entry; |
|
for (int i = 0; i < entries.size(); i++) { |
|
entry = entries.elementAt(i); |
|
if (entry.service.match(service)) { |
|
if (EType.isSupported(entry.keyType)) { |
|
return true; |
|
} else if (DEBUG) { |
|
System.out.println("Found unsupported keytype (" + |
|
entry.keyType + ") for " + service); |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
public String tabName() { |
|
return tabName; |
|
} |
|
|
|
/////////////////// THE WRITE SIDE /////////////////////// |
|
/////////////// only used by ktab tool ////////////////// |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void addEntry(PrincipalName service, char[] psswd, |
|
int kvno, boolean append) throws KrbException { |
|
addEntry(service, service.getSalt(), psswd, kvno, append); |
|
} |
|
|
|
|
|
public void addEntry(PrincipalName service, String salt, char[] psswd, |
|
int kvno, boolean append) throws KrbException { |
|
|
|
EncryptionKey[] encKeys = EncryptionKey.acquireSecretKeys( |
|
psswd, salt); |
|
|
|
// There should be only one maximum KVNO value for all etypes, so that |
|
// all added keys can have the same KVNO. |
|
|
|
int maxKvno = 0; |
|
for (int i = entries.size()-1; i >= 0; i--) { |
|
KeyTabEntry e = entries.get(i); |
|
if (e.service.match(service)) { |
|
if (e.keyVersion > maxKvno) { |
|
maxKvno = e.keyVersion; |
|
} |
|
if (!append || e.keyVersion == kvno) { |
|
entries.removeElementAt(i); |
|
} |
|
} |
|
} |
|
if (kvno == -1) { |
|
kvno = maxKvno + 1; |
|
} |
|
|
|
for (int i = 0; encKeys != null && i < encKeys.length; i++) { |
|
int keyType = encKeys[i].getEType(); |
|
byte[] keyValue = encKeys[i].getBytes(); |
|
|
|
KeyTabEntry newEntry = new KeyTabEntry(service, |
|
service.getRealm(), |
|
new KerberosTime(System.currentTimeMillis()), |
|
kvno, keyType, keyValue); |
|
entries.addElement(newEntry); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public KeyTabEntry[] getEntries() { |
|
KeyTabEntry[] kentries = new KeyTabEntry[entries.size()]; |
|
for (int i = 0; i < kentries.length; i++) { |
|
kentries[i] = entries.elementAt(i); |
|
} |
|
return kentries; |
|
} |
|
|
|
|
|
|
|
*/ |
|
public synchronized static KeyTab create() |
|
throws IOException, RealmException { |
|
String dname = getDefaultTabName(); |
|
return create(dname); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public synchronized static KeyTab create(String name) |
|
throws IOException, RealmException { |
|
|
|
try (KeyTabOutputStream kos = |
|
new KeyTabOutputStream(new FileOutputStream(name))) { |
|
kos.writeVersion(KRB5_KT_VNO); |
|
} |
|
return new KeyTab(name); |
|
} |
|
|
|
|
|
|
|
*/ |
|
public synchronized void save() throws IOException { |
|
try (KeyTabOutputStream kos = |
|
new KeyTabOutputStream(new FileOutputStream(tabName))) { |
|
kos.writeVersion(kt_vno); |
|
for (int i = 0; i < entries.size(); i++) { |
|
kos.writeEntry(entries.elementAt(i)); |
|
} |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int deleteEntries(PrincipalName service, int etype, int kvno) { |
|
int count = 0; |
|
|
|
|
|
Map<Integer,Integer> highest = new HashMap<>(); |
|
|
|
for (int i = entries.size()-1; i >= 0; i--) { |
|
KeyTabEntry e = entries.get(i); |
|
if (service.match(e.getService())) { |
|
if (etype == -1 || e.keyType == etype) { |
|
if (kvno == -2) { |
|
// Two rounds for kvno == -2. In the first round (here), |
|
|
|
if (highest.containsKey(e.keyType)) { |
|
int n = highest.get(e.keyType); |
|
if (e.keyVersion > n) { |
|
highest.put(e.keyType, e.keyVersion); |
|
} |
|
} else { |
|
highest.put(e.keyType, e.keyVersion); |
|
} |
|
} else if (kvno == -1 || e.keyVersion == kvno) { |
|
entries.removeElementAt(i); |
|
count++; |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
if (kvno == -2) { |
|
for (int i = entries.size()-1; i >= 0; i--) { |
|
KeyTabEntry e = entries.get(i); |
|
if (service.match(e.getService())) { |
|
if (etype == -1 || e.keyType == etype) { |
|
int n = highest.get(e.keyType); |
|
if (e.keyVersion != n) { |
|
entries.removeElementAt(i); |
|
count++; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
return count; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public synchronized void createVersion(File file) throws IOException { |
|
try (KeyTabOutputStream kos = |
|
new KeyTabOutputStream(new FileOutputStream(file))) { |
|
kos.write16(KRB5_KT_VNO); |
|
} |
|
} |
|
} |