|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
package sun.security.krb5; |
|
|
|
import java.io.*; |
|
import java.net.InetAddress; |
|
import java.net.UnknownHostException; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedExceptionAction; |
|
import java.util.ArrayList; |
|
import java.util.Arrays; |
|
import java.util.Hashtable; |
|
import java.util.List; |
|
import java.util.Locale; |
|
import java.util.StringTokenizer; |
|
import java.util.Vector; |
|
import java.util.regex.Matcher; |
|
import java.util.regex.Pattern; |
|
import sun.net.dns.ResolverConfiguration; |
|
import sun.security.krb5.internal.crypto.EType; |
|
import sun.security.krb5.internal.Krb5; |
|
import sun.security.util.SecurityProperties; |
|
|
|
/** |
|
* This class maintains key-value pairs of Kerberos configurable constants |
|
* from configuration file or from user specified system properties. |
|
*/ |
|
|
|
public class Config { |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final boolean DISABLE_REFERRALS; |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static final int MAX_REFERRALS; |
|
|
|
static { |
|
String disableReferralsProp = |
|
SecurityProperties.privilegedGetOverridable( |
|
"sun.security.krb5.disableReferrals"); |
|
if (disableReferralsProp != null) { |
|
DISABLE_REFERRALS = "true".equalsIgnoreCase(disableReferralsProp); |
|
} else { |
|
DISABLE_REFERRALS = false; |
|
} |
|
|
|
int maxReferralsValue = 5; |
|
String maxReferralsProp = |
|
SecurityProperties.privilegedGetOverridable( |
|
"sun.security.krb5.maxReferrals"); |
|
try { |
|
maxReferralsValue = Integer.parseInt(maxReferralsProp); |
|
} catch (NumberFormatException e) { |
|
} |
|
MAX_REFERRALS = maxReferralsValue; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private static Config singleton = null; |
|
|
|
|
|
|
|
*/ |
|
private Hashtable<String,Object> stanzaTable = new Hashtable<>(); |
|
|
|
private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG; |
|
|
|
|
|
private static final int BASE16_0 = 1; |
|
private static final int BASE16_1 = 16; |
|
private static final int BASE16_2 = 16 * 16; |
|
private static final int BASE16_3 = 16 * 16 * 16; |
|
|
|
|
|
|
|
*/ |
|
private final String defaultRealm; |
|
private final String defaultKDC; |
|
|
|
|
|
private static native String getWindowsDirectory(boolean isSystem); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static synchronized Config getInstance() throws KrbException { |
|
if (singleton == null) { |
|
singleton = new Config(); |
|
} |
|
return singleton; |
|
} |
|
|
|
/** |
|
* Refresh and reload the Configuration. This could involve, |
|
* for example reading the Configuration file again or getting |
|
* the java.security.krb5.* system properties again. This method |
|
* also tries its best to update static fields in other classes |
|
* that depend on the configuration. |
|
* |
|
* @exception KrbException if error occurs when constructing a Config |
|
* instance. Possible causes would be either of java.security.krb5.realm or |
|
* java.security.krb5.kdc not specified, error reading configuration file. |
|
*/ |
|
|
|
public static void refresh() throws KrbException { |
|
synchronized (Config.class) { |
|
singleton = new Config(); |
|
} |
|
KdcComm.initStatic(); |
|
EType.initStatic(); |
|
Checksum.initStatic(); |
|
KrbAsReqBuilder.ReferralsState.initStatic(); |
|
} |
|
|
|
|
|
private static boolean isMacosLionOrBetter() { |
|
|
|
String osname = getProperty("os.name"); |
|
if (!osname.contains("OS X")) { |
|
return false; |
|
} |
|
|
|
String osVersion = getProperty("os.version"); |
|
String[] fragments = osVersion.split("\\."); |
|
|
|
|
|
if (!fragments[0].equals("10")) return false; |
|
if (fragments.length < 2) return false; |
|
|
|
|
|
try { |
|
int minorVers = Integer.parseInt(fragments[1]); |
|
if (minorVers >= 7) return true; |
|
} catch (NumberFormatException e) { |
|
// was not an integer |
|
} |
|
|
|
return false; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private Config() throws KrbException { |
|
|
|
|
|
*/ |
|
String tmp = getProperty("java.security.krb5.kdc"); |
|
if (tmp != null) { |
|
|
|
defaultKDC = tmp.replace(':', ' '); |
|
} else { |
|
defaultKDC = null; |
|
} |
|
defaultRealm = getProperty("java.security.krb5.realm"); |
|
if ((defaultKDC == null && defaultRealm != null) || |
|
(defaultRealm == null && defaultKDC != null)) { |
|
throw new KrbException |
|
("System property java.security.krb5.kdc and " + |
|
"java.security.krb5.realm both must be set or " + |
|
"neither must be set."); |
|
} |
|
|
|
|
|
try { |
|
List<String> configFile; |
|
String fileName = getJavaFileName(); |
|
if (fileName != null) { |
|
configFile = loadConfigFile(fileName); |
|
stanzaTable = parseStanzaTable(configFile); |
|
if (DEBUG) { |
|
System.out.println("Loaded from Java config"); |
|
} |
|
} else { |
|
boolean found = false; |
|
if (isMacosLionOrBetter()) { |
|
try { |
|
stanzaTable = SCDynamicStoreConfig.getConfig(); |
|
if (DEBUG) { |
|
System.out.println("Loaded from SCDynamicStoreConfig"); |
|
} |
|
found = true; |
|
} catch (IOException ioe) { |
|
// OK. Will go on with file |
|
} |
|
} |
|
if (!found) { |
|
fileName = getNativeFileName(); |
|
configFile = loadConfigFile(fileName); |
|
stanzaTable = parseStanzaTable(configFile); |
|
if (DEBUG) { |
|
System.out.println("Loaded from native config"); |
|
} |
|
} |
|
} |
|
} catch (IOException ioe) { |
|
// I/O error, mostly like krb5.conf missing. |
|
// No problem. We'll use DNS or system property etc. |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String get(String... keys) { |
|
Vector<String> v = getString0(keys); |
|
if (v == null) return null; |
|
return v.lastElement(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public Boolean getBooleanObject(String... keys) { |
|
String s = get(keys); |
|
if (s == null) { |
|
return null; |
|
} |
|
switch (s.toLowerCase(Locale.US)) { |
|
case "yes": case "true": |
|
return Boolean.TRUE; |
|
case "no": case "false": |
|
return Boolean.FALSE; |
|
default: |
|
return null; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getAll(String... keys) { |
|
Vector<String> v = getString0(keys); |
|
if (v == null) return null; |
|
StringBuilder sb = new StringBuilder(); |
|
boolean first = true; |
|
for (String s: v) { |
|
s = s.replaceAll("[\\s,]+", " "); |
|
if (first) { |
|
sb.append(s); |
|
first = false; |
|
} else { |
|
sb.append(' ').append(s); |
|
} |
|
} |
|
return sb.toString(); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean exists(String... keys) { |
|
return get0(keys) != null; |
|
} |
|
|
|
|
|
@SuppressWarnings("unchecked") |
|
private Vector<String> getString0(String... keys) { |
|
try { |
|
return (Vector<String>)get0(keys); |
|
} catch (ClassCastException cce) { |
|
throw new IllegalArgumentException(cce); |
|
} |
|
} |
|
|
|
// Internal method. Returns the value for keys, which can be a sub-stanza |
|
// or final string value(s). |
|
|
|
@SuppressWarnings("unchecked") |
|
private Object get0(String... keys) { |
|
Object current = stanzaTable; |
|
try { |
|
for (String key: keys) { |
|
current = ((Hashtable<String,Object>)current).get(key); |
|
if (current == null) return null; |
|
} |
|
return current; |
|
} catch (ClassCastException cce) { |
|
throw new IllegalArgumentException(cce); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public static int duration(String s) throws KrbException { |
|
|
|
if (s.isEmpty()) { |
|
throw new KrbException("Duration cannot be empty"); |
|
} |
|
|
|
|
|
if (s.matches("\\d+")) { |
|
return Integer.parseInt(s); |
|
} |
|
|
|
|
|
Matcher m = Pattern.compile("(\\d+):(\\d+)(:(\\d+))?").matcher(s); |
|
if (m.matches()) { |
|
int hr = Integer.parseInt(m.group(1)); |
|
int min = Integer.parseInt(m.group(2)); |
|
if (min >= 60) { |
|
throw new KrbException("Illegal duration format " + s); |
|
} |
|
int result = hr * 3600 + min * 60; |
|
if (m.group(4) != null) { |
|
int sec = Integer.parseInt(m.group(4)); |
|
if (sec >= 60) { |
|
throw new KrbException("Illegal duration format " + s); |
|
} |
|
result += sec; |
|
} |
|
return result; |
|
} |
|
|
|
// NdNhNmNs |
|
|
|
m = Pattern.compile( |
|
"((\\d+)d)?\\s*((\\d+)h)?\\s*((\\d+)m)?\\s*((\\d+)s)?", |
|
Pattern.CASE_INSENSITIVE).matcher(s); |
|
if (m.matches()) { |
|
int result = 0; |
|
if (m.group(2) != null) { |
|
result += 86400 * Integer.parseInt(m.group(2)); |
|
} |
|
if (m.group(4) != null) { |
|
result += 3600 * Integer.parseInt(m.group(4)); |
|
} |
|
if (m.group(6) != null) { |
|
result += 60 * Integer.parseInt(m.group(6)); |
|
} |
|
if (m.group(8) != null) { |
|
result += Integer.parseInt(m.group(8)); |
|
} |
|
return result; |
|
} |
|
|
|
throw new KrbException("Illegal duration format " + s); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int getIntValue(String... keys) { |
|
String result = get(keys); |
|
int value = Integer.MIN_VALUE; |
|
if (result != null) { |
|
try { |
|
value = parseIntValue(result); |
|
} catch (NumberFormatException e) { |
|
if (DEBUG) { |
|
System.out.println("Exception in getting value of " + |
|
Arrays.toString(keys) + " " + |
|
e.getMessage()); |
|
System.out.println("Setting " + Arrays.toString(keys) + |
|
" to minimum value"); |
|
} |
|
value = Integer.MIN_VALUE; |
|
} |
|
} |
|
return value; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean getBooleanValue(String... keys) { |
|
String val = get(keys); |
|
if (val != null && val.equalsIgnoreCase("true")) { |
|
return true; |
|
} else { |
|
return false; |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private int parseIntValue(String input) throws NumberFormatException { |
|
int value = 0; |
|
if (input.startsWith("+")) { |
|
String temp = input.substring(1); |
|
return Integer.parseInt(temp); |
|
} else if (input.startsWith("0x")) { |
|
String temp = input.substring(2); |
|
char[] chars = temp.toCharArray(); |
|
if (chars.length > 8) { |
|
throw new NumberFormatException(); |
|
} else { |
|
for (int i = 0; i < chars.length; i++) { |
|
int index = chars.length - i - 1; |
|
switch (chars[i]) { |
|
case '0': |
|
value += 0; |
|
break; |
|
case '1': |
|
value += 1 * getBase(index); |
|
break; |
|
case '2': |
|
value += 2 * getBase(index); |
|
break; |
|
case '3': |
|
value += 3 * getBase(index); |
|
break; |
|
case '4': |
|
value += 4 * getBase(index); |
|
break; |
|
case '5': |
|
value += 5 * getBase(index); |
|
break; |
|
case '6': |
|
value += 6 * getBase(index); |
|
break; |
|
case '7': |
|
value += 7 * getBase(index); |
|
break; |
|
case '8': |
|
value += 8 * getBase(index); |
|
break; |
|
case '9': |
|
value += 9 * getBase(index); |
|
break; |
|
case 'a': |
|
case 'A': |
|
value += 10 * getBase(index); |
|
break; |
|
case 'b': |
|
case 'B': |
|
value += 11 * getBase(index); |
|
break; |
|
case 'c': |
|
case 'C': |
|
value += 12 * getBase(index); |
|
break; |
|
case 'd': |
|
case 'D': |
|
value += 13 * getBase(index); |
|
break; |
|
case 'e': |
|
case 'E': |
|
value += 14 * getBase(index); |
|
break; |
|
case 'f': |
|
case 'F': |
|
value += 15 * getBase(index); |
|
break; |
|
default: |
|
throw new NumberFormatException("Invalid numerical format"); |
|
} |
|
} |
|
} |
|
if (value < 0) { |
|
throw new NumberFormatException("Data overflow."); |
|
} |
|
} else { |
|
value = Integer.parseInt(input); |
|
} |
|
return value; |
|
} |
|
|
|
private int getBase(int i) { |
|
int result = 16; |
|
switch (i) { |
|
case 0: |
|
result = BASE16_0; |
|
break; |
|
case 1: |
|
result = BASE16_1; |
|
break; |
|
case 2: |
|
result = BASE16_2; |
|
break; |
|
case 3: |
|
result = BASE16_3; |
|
break; |
|
default: |
|
for (int j = 1; j < i; j++) { |
|
result *= 16; |
|
} |
|
} |
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private List<String> loadConfigFile(final String fileName) |
|
throws IOException, KrbException { |
|
try { |
|
List<String> v = new ArrayList<>(); |
|
try (BufferedReader br = new BufferedReader(new InputStreamReader( |
|
AccessController.doPrivileged( |
|
new PrivilegedExceptionAction<FileInputStream> () { |
|
public FileInputStream run() throws IOException { |
|
return new FileInputStream(fileName); |
|
} |
|
})))) { |
|
String line; |
|
String previous = null; |
|
while ((line = br.readLine()) != null) { |
|
line = line.trim(); |
|
if (line.isEmpty() || line.startsWith("#") || line.startsWith(";")) { |
|
// ignore comments and blank line |
|
|
|
continue; |
|
} |
|
// In practice, a subsection might look like: |
|
// [realms] |
|
// EXAMPLE.COM = |
|
// { |
|
// kdc = kerberos.example.com |
|
// ... |
|
// } |
|
// Before parsed into stanza table, it needs to be |
|
// converted into a canonicalized style (no indent): |
|
// realms = { |
|
// EXAMPLE.COM = { |
|
// kdc = kerberos.example.com |
|
// ... |
|
// } |
|
// } |
|
|
|
if (line.startsWith("[")) { |
|
if (!line.endsWith("]")) { |
|
throw new KrbException("Illegal config content:" |
|
+ line); |
|
} |
|
if (previous != null) { |
|
v.add(previous); |
|
v.add("}"); |
|
} |
|
String title = line.substring( |
|
1, line.length()-1).trim(); |
|
if (title.isEmpty()) { |
|
throw new KrbException("Illegal config content:" |
|
+ line); |
|
} |
|
previous = title + " = {"; |
|
} else if (line.startsWith("{")) { |
|
if (previous == null) { |
|
throw new KrbException( |
|
"Config file should not start with \"{\""); |
|
} |
|
previous += " {"; |
|
if (line.length() > 1) { |
|
|
|
v.add(previous); |
|
previous = line.substring(1).trim(); |
|
} |
|
} else { |
|
|
|
if (previous != null) { |
|
v.add(previous); |
|
previous = line; |
|
} |
|
} |
|
} |
|
if (previous != null) { |
|
v.add(previous); |
|
v.add("}"); |
|
} |
|
} |
|
return v; |
|
} catch (java.security.PrivilegedActionException pe) { |
|
throw (IOException)pe.getException(); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
@SuppressWarnings("unchecked") |
|
private Hashtable<String,Object> parseStanzaTable(List<String> v) |
|
throws KrbException { |
|
Hashtable<String,Object> current = stanzaTable; |
|
for (String line: v) { |
|
// There are 3 kinds of lines |
|
// 1. a = b |
|
// 2. a = { |
|
|
|
if (line.equals("}")) { |
|
|
|
current = (Hashtable<String,Object>)current.remove(" PARENT "); |
|
if (current == null) { |
|
throw new KrbException("Unmatched close brace"); |
|
} |
|
} else { |
|
int pos = line.indexOf('='); |
|
if (pos < 0) { |
|
throw new KrbException("Illegal config content:" + line); |
|
} |
|
String key = line.substring(0, pos).trim(); |
|
String value = trimmed(line.substring(pos+1)); |
|
if (value.equals("{")) { |
|
Hashtable<String,Object> subTable; |
|
if (current == stanzaTable) { |
|
key = key.toLowerCase(Locale.US); |
|
} |
|
subTable = new Hashtable<>(); |
|
current.put(key, subTable); |
|
// A special entry for its parent. Put whitespaces around, |
|
|
|
subTable.put(" PARENT ", current); |
|
current = subTable; |
|
} else { |
|
Vector<String> values; |
|
if (current.containsKey(key)) { |
|
Object obj = current.get(key); |
|
// If a key first shows as a section and then a value, |
|
// this is illegal. However, we haven't really forbid |
|
// first value then section, which the final result |
|
|
|
if (!(obj instanceof Vector)) { |
|
throw new KrbException("Key " + key |
|
+ "used for both value and section"); |
|
} |
|
values = (Vector<String>)current.get(key); |
|
} else { |
|
values = new Vector<String>(); |
|
current.put(key, values); |
|
} |
|
values.add(value); |
|
} |
|
} |
|
} |
|
if (current != stanzaTable) { |
|
throw new KrbException("Not closed"); |
|
} |
|
return current; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private String getJavaFileName() { |
|
String name = getProperty("java.security.krb5.conf"); |
|
if (name == null) { |
|
name = getProperty("java.home") + File.separator + |
|
"lib" + File.separator + "security" + |
|
File.separator + "krb5.conf"; |
|
if (!fileExists(name)) { |
|
name = null; |
|
} |
|
} |
|
if (DEBUG) { |
|
System.out.println("Java config name: " + name); |
|
} |
|
return name; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private String getNativeFileName() { |
|
String name = null; |
|
String osname = getProperty("os.name"); |
|
if (osname.startsWith("Windows")) { |
|
try { |
|
Credentials.ensureLoaded(); |
|
} catch (Exception e) { |
|
// ignore exceptions |
|
} |
|
if (Credentials.alreadyLoaded) { |
|
String path = getWindowsDirectory(false); |
|
if (path != null) { |
|
if (path.endsWith("\\")) { |
|
path = path + "krb5.ini"; |
|
} else { |
|
path = path + "\\krb5.ini"; |
|
} |
|
if (fileExists(path)) { |
|
name = path; |
|
} |
|
} |
|
if (name == null) { |
|
path = getWindowsDirectory(true); |
|
if (path != null) { |
|
if (path.endsWith("\\")) { |
|
path = path + "krb5.ini"; |
|
} else { |
|
path = path + "\\krb5.ini"; |
|
} |
|
name = path; |
|
} |
|
} |
|
} |
|
if (name == null) { |
|
name = "c:\\winnt\\krb5.ini"; |
|
} |
|
} else if (osname.startsWith("SunOS")) { |
|
name = "/etc/krb5/krb5.conf"; |
|
} else if (osname.contains("OS X")) { |
|
name = findMacosConfigFile(); |
|
} else { |
|
name = "/etc/krb5.conf"; |
|
} |
|
if (DEBUG) { |
|
System.out.println("Native config name: " + name); |
|
} |
|
return name; |
|
} |
|
|
|
private static String getProperty(String property) { |
|
return java.security.AccessController.doPrivileged( |
|
new sun.security.action.GetPropertyAction(property)); |
|
} |
|
|
|
private String findMacosConfigFile() { |
|
String userHome = getProperty("user.home"); |
|
final String PREF_FILE = "/Library/Preferences/edu.mit.Kerberos"; |
|
String userPrefs = userHome + PREF_FILE; |
|
|
|
if (fileExists(userPrefs)) { |
|
return userPrefs; |
|
} |
|
|
|
if (fileExists(PREF_FILE)) { |
|
return PREF_FILE; |
|
} |
|
|
|
return "/etc/krb5.conf"; |
|
} |
|
|
|
private static String trimmed(String s) { |
|
s = s.trim(); |
|
if (s.length() >= 2 && |
|
((s.charAt(0) == '"' && s.charAt(s.length()-1) == '"') || |
|
(s.charAt(0) == '\'' && s.charAt(s.length()-1) == '\''))) { |
|
s = s.substring(1, s.length()-1).trim(); |
|
} |
|
return s; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public void listTable() { |
|
System.out.println(this); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public int[] defaultEtype(String configName) throws KrbException { |
|
String default_enctypes; |
|
default_enctypes = get("libdefaults", configName); |
|
int[] etype; |
|
if (default_enctypes == null) { |
|
if (DEBUG) { |
|
System.out.println("Using builtin default etypes for " + |
|
configName); |
|
} |
|
etype = EType.getBuiltInDefaults(); |
|
} else { |
|
String delim = " "; |
|
StringTokenizer st; |
|
for (int j = 0; j < default_enctypes.length(); j++) { |
|
if (default_enctypes.substring(j, j + 1).equals(",")) { |
|
// only two delimiters are allowed to use |
|
|
|
delim = ","; |
|
break; |
|
} |
|
} |
|
st = new StringTokenizer(default_enctypes, delim); |
|
int len = st.countTokens(); |
|
ArrayList<Integer> ls = new ArrayList<>(len); |
|
int type; |
|
for (int i = 0; i < len; i++) { |
|
type = Config.getType(st.nextToken()); |
|
if (type != -1 && EType.isSupported(type)) { |
|
ls.add(type); |
|
} |
|
} |
|
if (ls.isEmpty()) { |
|
throw new KrbException("no supported default etypes for " |
|
+ configName); |
|
} else { |
|
etype = new int[ls.size()]; |
|
for (int i = 0; i < etype.length; i++) { |
|
etype[i] = ls.get(i); |
|
} |
|
} |
|
} |
|
|
|
if (DEBUG) { |
|
System.out.print("default etypes for " + configName + ":"); |
|
for (int i = 0; i < etype.length; i++) { |
|
System.out.print(" " + etype[i]); |
|
} |
|
System.out.println("."); |
|
} |
|
return etype; |
|
} |
|
|
|
|
|
/** |
|
* Get the etype and checksum value for the specified encryption and |
|
* checksum type. |
|
* |
|
*/ |
|
|
|
|
|
|
|
|
|
*/ |
|
public static int getType(String input) { |
|
int result = -1; |
|
if (input == null) { |
|
return result; |
|
} |
|
if (input.startsWith("d") || (input.startsWith("D"))) { |
|
if (input.equalsIgnoreCase("des-cbc-crc")) { |
|
result = EncryptedData.ETYPE_DES_CBC_CRC; |
|
} else if (input.equalsIgnoreCase("des-cbc-md5")) { |
|
result = EncryptedData.ETYPE_DES_CBC_MD5; |
|
} else if (input.equalsIgnoreCase("des-mac")) { |
|
result = Checksum.CKSUMTYPE_DES_MAC; |
|
} else if (input.equalsIgnoreCase("des-mac-k")) { |
|
result = Checksum.CKSUMTYPE_DES_MAC_K; |
|
} else if (input.equalsIgnoreCase("des-cbc-md4")) { |
|
result = EncryptedData.ETYPE_DES_CBC_MD4; |
|
} else if (input.equalsIgnoreCase("des3-cbc-sha1") || |
|
input.equalsIgnoreCase("des3-hmac-sha1") || |
|
input.equalsIgnoreCase("des3-cbc-sha1-kd") || |
|
input.equalsIgnoreCase("des3-cbc-hmac-sha1-kd")) { |
|
result = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD; |
|
} |
|
} else if (input.startsWith("a") || (input.startsWith("A"))) { |
|
|
|
if (input.equalsIgnoreCase("aes128-cts") || |
|
input.equalsIgnoreCase("aes128-cts-hmac-sha1-96")) { |
|
result = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96; |
|
} else if (input.equalsIgnoreCase("aes256-cts") || |
|
input.equalsIgnoreCase("aes256-cts-hmac-sha1-96")) { |
|
result = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96; |
|
// ARCFOUR-HMAC |
|
} else if (input.equalsIgnoreCase("arcfour-hmac") || |
|
input.equalsIgnoreCase("arcfour-hmac-md5")) { |
|
result = EncryptedData.ETYPE_ARCFOUR_HMAC; |
|
} |
|
// RC4-HMAC |
|
} else if (input.equalsIgnoreCase("rc4-hmac")) { |
|
result = EncryptedData.ETYPE_ARCFOUR_HMAC; |
|
} else if (input.equalsIgnoreCase("CRC32")) { |
|
result = Checksum.CKSUMTYPE_CRC32; |
|
} else if (input.startsWith("r") || (input.startsWith("R"))) { |
|
if (input.equalsIgnoreCase("rsa-md5")) { |
|
result = Checksum.CKSUMTYPE_RSA_MD5; |
|
} else if (input.equalsIgnoreCase("rsa-md5-des")) { |
|
result = Checksum.CKSUMTYPE_RSA_MD5_DES; |
|
} |
|
} else if (input.equalsIgnoreCase("hmac-sha1-des3-kd")) { |
|
result = Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD; |
|
} else if (input.equalsIgnoreCase("hmac-sha1-96-aes128")) { |
|
result = Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128; |
|
} else if (input.equalsIgnoreCase("hmac-sha1-96-aes256")) { |
|
result = Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256; |
|
} else if (input.equalsIgnoreCase("hmac-md5-rc4") || |
|
input.equalsIgnoreCase("hmac-md5-arcfour") || |
|
input.equalsIgnoreCase("hmac-md5-enc")) { |
|
result = Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR; |
|
} else if (input.equalsIgnoreCase("NULL")) { |
|
result = EncryptedData.ETYPE_NULL; |
|
} |
|
|
|
return result; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public void resetDefaultRealm(String realm) { |
|
if (DEBUG) { |
|
System.out.println(">>> Config try resetting default kdc " + realm); |
|
} |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
public boolean useAddresses() { |
|
boolean useAddr = false; |
|
|
|
String value = get("libdefaults", "no_addresses"); |
|
useAddr = (value != null && value.equalsIgnoreCase("false")); |
|
if (useAddr == false) { |
|
|
|
value = get("libdefaults", "noaddresses"); |
|
useAddr = (value != null && value.equalsIgnoreCase("false")); |
|
} |
|
return useAddr; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private boolean useDNS(String name, boolean defaultValue) { |
|
Boolean value = getBooleanObject("libdefaults", name); |
|
if (value != null) { |
|
return value.booleanValue(); |
|
} |
|
value = getBooleanObject("libdefaults", "dns_fallback"); |
|
if (value != null) { |
|
return value.booleanValue(); |
|
} |
|
return defaultValue; |
|
} |
|
|
|
|
|
|
|
*/ |
|
private boolean useDNS_KDC() { |
|
return useDNS("dns_lookup_kdc", true); |
|
} |
|
|
|
|
|
|
|
*/ |
|
private boolean useDNS_Realm() { |
|
return useDNS("dns_lookup_realm", false); |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getDefaultRealm() throws KrbException { |
|
if (defaultRealm != null) { |
|
return defaultRealm; |
|
} |
|
Exception cause = null; |
|
String realm = get("libdefaults", "default_realm"); |
|
if ((realm == null) && useDNS_Realm()) { |
|
|
|
try { |
|
realm = getRealmFromDNS(); |
|
} catch (KrbException ke) { |
|
cause = ke; |
|
} |
|
} |
|
if (realm == null) { |
|
realm = java.security.AccessController.doPrivileged( |
|
new java.security.PrivilegedAction<String>() { |
|
@Override |
|
public String run() { |
|
String osname = System.getProperty("os.name"); |
|
if (osname.startsWith("Windows")) { |
|
return System.getenv("USERDNSDOMAIN"); |
|
} |
|
return null; |
|
} |
|
}); |
|
} |
|
if (realm == null) { |
|
KrbException ke = new KrbException("Cannot locate default realm"); |
|
if (cause != null) { |
|
ke.initCause(cause); |
|
} |
|
throw ke; |
|
} |
|
return realm; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
public String getKDCList(String realm) throws KrbException { |
|
if (realm == null) { |
|
realm = getDefaultRealm(); |
|
} |
|
if (realm.equalsIgnoreCase(defaultRealm)) { |
|
return defaultKDC; |
|
} |
|
Exception cause = null; |
|
String kdcs = getAll("realms", realm, "kdc"); |
|
if ((kdcs == null) && useDNS_KDC()) { |
|
|
|
try { |
|
kdcs = getKDCFromDNS(realm); |
|
} catch (KrbException ke) { |
|
cause = ke; |
|
} |
|
} |
|
if (kdcs == null) { |
|
kdcs = java.security.AccessController.doPrivileged( |
|
new java.security.PrivilegedAction<String>() { |
|
@Override |
|
public String run() { |
|
String osname = System.getProperty("os.name"); |
|
if (osname.startsWith("Windows")) { |
|
String logonServer = System.getenv("LOGONSERVER"); |
|
if (logonServer != null |
|
&& logonServer.startsWith("\\\\")) { |
|
logonServer = logonServer.substring(2); |
|
} |
|
return logonServer; |
|
} |
|
return null; |
|
} |
|
}); |
|
} |
|
if (kdcs == null) { |
|
if (defaultKDC != null) { |
|
return defaultKDC; |
|
} |
|
KrbException ke = new KrbException("Cannot locate KDC"); |
|
if (cause != null) { |
|
ke.initCause(cause); |
|
} |
|
throw ke; |
|
} |
|
return kdcs; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private String getRealmFromDNS() throws KrbException { |
|
|
|
String realm = null; |
|
String hostName = null; |
|
try { |
|
hostName = InetAddress.getLocalHost().getCanonicalHostName(); |
|
} catch (UnknownHostException e) { |
|
KrbException ke = new KrbException(Krb5.KRB_ERR_GENERIC, |
|
"Unable to locate Kerberos realm: " + e.getMessage()); |
|
ke.initCause(e); |
|
throw (ke); |
|
} |
|
|
|
String mapRealm = PrincipalName.mapHostToRealm(hostName); |
|
if (mapRealm == null) { |
|
|
|
List<String> srchlist = ResolverConfiguration.open().searchlist(); |
|
for (String domain: srchlist) { |
|
realm = checkRealm(domain); |
|
if (realm != null) { |
|
break; |
|
} |
|
} |
|
} else { |
|
realm = checkRealm(mapRealm); |
|
} |
|
if (realm == null) { |
|
throw new KrbException(Krb5.KRB_ERR_GENERIC, |
|
"Unable to locate Kerberos realm"); |
|
} |
|
return realm; |
|
} |
|
|
|
|
|
|
|
|
|
*/ |
|
private static String checkRealm(String mapRealm) { |
|
if (DEBUG) { |
|
System.out.println("getRealmFromDNS: trying " + mapRealm); |
|
} |
|
String[] records = null; |
|
String newRealm = mapRealm; |
|
while ((records == null) && (newRealm != null)) { |
|
|
|
records = KrbServiceLocator.getKerberosService(newRealm); |
|
newRealm = Realm.parseRealmComponent(newRealm); |
|
// if no DNS TXT records found, try again using sub-realm |
|
} |
|
if (records != null) { |
|
for (int i = 0; i < records.length; i++) { |
|
if (records[i].equalsIgnoreCase(mapRealm)) { |
|
return records[i]; |
|
} |
|
} |
|
} |
|
return null; |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
private String getKDCFromDNS(String realm) throws KrbException { |
|
|
|
String kdcs = ""; |
|
String[] srvs = null; |
|
|
|
if (DEBUG) { |
|
System.out.println("getKDCFromDNS using UDP"); |
|
} |
|
srvs = KrbServiceLocator.getKerberosService(realm, "_udp"); |
|
if (srvs == null) { |
|
|
|
if (DEBUG) { |
|
System.out.println("getKDCFromDNS using TCP"); |
|
} |
|
srvs = KrbServiceLocator.getKerberosService(realm, "_tcp"); |
|
} |
|
if (srvs == null) { |
|
|
|
throw new KrbException(Krb5.KRB_ERR_GENERIC, |
|
"Unable to locate KDC for realm " + realm); |
|
} |
|
if (srvs.length == 0) { |
|
return null; |
|
} |
|
for (int i = 0; i < srvs.length; i++) { |
|
kdcs += srvs[i].trim() + " "; |
|
} |
|
kdcs = kdcs.trim(); |
|
if (kdcs.equals("")) { |
|
return null; |
|
} |
|
return kdcs; |
|
} |
|
|
|
private boolean fileExists(String name) { |
|
return java.security.AccessController.doPrivileged( |
|
new FileExistsAction(name)); |
|
} |
|
|
|
static class FileExistsAction |
|
implements java.security.PrivilegedAction<Boolean> { |
|
|
|
private String fileName; |
|
|
|
public FileExistsAction(String fileName) { |
|
this.fileName = fileName; |
|
} |
|
|
|
public Boolean run() { |
|
return new File(fileName).exists(); |
|
} |
|
} |
|
|
|
// Shows the content of the Config object for debug purpose. |
|
// |
|
// { |
|
// libdefaults = { |
|
// default_realm = R |
|
// } |
|
// realms = { |
|
// R = { |
|
// kdc = [k1,k2] |
|
// } |
|
// } |
|
// } |
|
|
|
@Override |
|
public String toString() { |
|
StringBuffer sb = new StringBuffer(); |
|
toStringInternal("", stanzaTable, sb); |
|
return sb.toString(); |
|
} |
|
private static void toStringInternal(String prefix, Object obj, |
|
StringBuffer sb) { |
|
if (obj instanceof String) { |
|
|
|
sb.append(obj).append('\n'); |
|
} else if (obj instanceof Hashtable) { |
|
|
|
Hashtable<?, ?> tab = (Hashtable<?, ?>)obj; |
|
sb.append("{\n"); |
|
for (Object o: tab.keySet()) { |
|
|
|
sb.append(prefix).append(" ").append(o).append(" = "); |
|
|
|
toStringInternal(prefix + " ", tab.get(o), sb); |
|
} |
|
sb.append(prefix).append("}\n"); |
|
} else if (obj instanceof Vector) { |
|
|
|
Vector<?> v = (Vector<?>)obj; |
|
sb.append("["); |
|
boolean first = true; |
|
for (Object o: v.toArray()) { |
|
if (!first) sb.append(","); |
|
sb.append(o); |
|
first = false; |
|
} |
|
sb.append("]\n"); |
|
} |
|
} |
|
} |