Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/share/classes/sun/security/krb5/Config.java
38830 views
/*1* Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.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 it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation. Oracle designates this7* particular file as subject to the "Classpath" exception as provided8* 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 WITHOUT11* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or12* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License13* version 2 for more details (a copy is included in the LICENSE file that14* accompanied this code).15*16* You should have received a copy of the GNU General Public License version17* 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 USA21* or visit www.oracle.com if you need additional information or have any22* questions.23*/2425/*26*27* (C) Copyright IBM Corp. 1999 All Rights Reserved.28* Copyright 1997 The Open Group Research Institute. All rights reserved.29*/30package sun.security.krb5;3132import java.io.*;33import java.net.InetAddress;34import java.net.UnknownHostException;35import java.security.AccessController;36import java.security.PrivilegedExceptionAction;37import java.util.ArrayList;38import java.util.Arrays;39import java.util.Hashtable;40import java.util.List;41import java.util.Locale;42import java.util.StringTokenizer;43import java.util.Vector;44import java.util.regex.Matcher;45import java.util.regex.Pattern;46import sun.net.dns.ResolverConfiguration;47import sun.security.krb5.internal.crypto.EType;48import sun.security.krb5.internal.Krb5;49import sun.security.util.SecurityProperties;5051/**52* This class maintains key-value pairs of Kerberos configurable constants53* from configuration file or from user specified system properties.54*/5556public class Config {5758/**59* {@systemProperty sun.security.krb5.disableReferrals} property60* indicating whether or not cross-realm referrals (RFC 6806) are61* enabled.62*/63public static final boolean DISABLE_REFERRALS;6465/**66* {@systemProperty sun.security.krb5.maxReferrals} property67* indicating the maximum number of cross-realm referral68* hops allowed.69*/70public static final int MAX_REFERRALS;7172static {73String disableReferralsProp =74SecurityProperties.privilegedGetOverridable(75"sun.security.krb5.disableReferrals");76if (disableReferralsProp != null) {77DISABLE_REFERRALS = "true".equalsIgnoreCase(disableReferralsProp);78} else {79DISABLE_REFERRALS = false;80}8182int maxReferralsValue = 5;83String maxReferralsProp =84SecurityProperties.privilegedGetOverridable(85"sun.security.krb5.maxReferrals");86try {87maxReferralsValue = Integer.parseInt(maxReferralsProp);88} catch (NumberFormatException e) {89}90MAX_REFERRALS = maxReferralsValue;91}9293/*94* Only allow a single instance of Config.95*/96private static Config singleton = null;9798/*99* Hashtable used to store configuration information.100*/101private Hashtable<String,Object> stanzaTable = new Hashtable<>();102103private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;104105// these are used for hexdecimal calculation.106private static final int BASE16_0 = 1;107private static final int BASE16_1 = 16;108private static final int BASE16_2 = 16 * 16;109private static final int BASE16_3 = 16 * 16 * 16;110111/**112* Specified by system properties. Must be both null or non-null.113*/114private final String defaultRealm;115private final String defaultKDC;116117// used for native interface118private static native String getWindowsDirectory(boolean isSystem);119120121/**122* Gets an instance of Config class. One and only one instance (the123* singleton) is returned.124*125* @exception KrbException if error occurs when constructing a Config126* instance. Possible causes would be either of java.security.krb5.realm or127* java.security.krb5.kdc not specified, error reading configuration file.128*/129public static synchronized Config getInstance() throws KrbException {130if (singleton == null) {131singleton = new Config();132}133return singleton;134}135136/**137* Refresh and reload the Configuration. This could involve,138* for example reading the Configuration file again or getting139* the java.security.krb5.* system properties again. This method140* also tries its best to update static fields in other classes141* that depend on the configuration.142*143* @exception KrbException if error occurs when constructing a Config144* instance. Possible causes would be either of java.security.krb5.realm or145* java.security.krb5.kdc not specified, error reading configuration file.146*/147148public static void refresh() throws KrbException {149synchronized (Config.class) {150singleton = new Config();151}152KdcComm.initStatic();153EType.initStatic();154Checksum.initStatic();155KrbAsReqBuilder.ReferralsState.initStatic();156}157158159private static boolean isMacosLionOrBetter() {160// split the "10.x.y" version number161String osname = getProperty("os.name");162if (!osname.contains("OS X")) {163return false;164}165166String osVersion = getProperty("os.version");167String[] fragments = osVersion.split("\\.");168169// sanity check the "10." part of the version170if (!fragments[0].equals("10")) return false;171if (fragments.length < 2) return false;172173// check if Mac OS X 10.7(.y)174try {175int minorVers = Integer.parseInt(fragments[1]);176if (minorVers >= 7) return true;177} catch (NumberFormatException e) {178// was not an integer179}180181return false;182}183184/**185* Private constructor - can not be instantiated externally.186*/187private Config() throws KrbException {188/*189* If either one system property is specified, we throw exception.190*/191String tmp = getProperty("java.security.krb5.kdc");192if (tmp != null) {193// The user can specify a list of kdc hosts separated by ":"194defaultKDC = tmp.replace(':', ' ');195} else {196defaultKDC = null;197}198defaultRealm = getProperty("java.security.krb5.realm");199if ((defaultKDC == null && defaultRealm != null) ||200(defaultRealm == null && defaultKDC != null)) {201throw new KrbException202("System property java.security.krb5.kdc and " +203"java.security.krb5.realm both must be set or " +204"neither must be set.");205}206207// Always read the Kerberos configuration file208try {209List<String> configFile;210String fileName = getJavaFileName();211if (fileName != null) {212configFile = loadConfigFile(fileName);213stanzaTable = parseStanzaTable(configFile);214if (DEBUG) {215System.out.println("Loaded from Java config");216}217} else {218boolean found = false;219if (isMacosLionOrBetter()) {220try {221stanzaTable = SCDynamicStoreConfig.getConfig();222if (DEBUG) {223System.out.println("Loaded from SCDynamicStoreConfig");224}225found = true;226} catch (IOException ioe) {227// OK. Will go on with file228}229}230if (!found) {231fileName = getNativeFileName();232configFile = loadConfigFile(fileName);233stanzaTable = parseStanzaTable(configFile);234if (DEBUG) {235System.out.println("Loaded from native config");236}237}238}239} catch (IOException ioe) {240// I/O error, mostly like krb5.conf missing.241// No problem. We'll use DNS or system property etc.242}243}244245/**246* Gets the last-defined string value for the specified keys.247* @param keys the keys, as an array from section name, sub-section names248* (if any), to value name.249* @return the value. When there are multiple values for the same key,250* returns the last one. {@code null} is returned if not all the keys are251* defined. For example, {@code get("libdefaults", "forwardable")} will252* return null if "forwardable" is not defined in [libdefaults], and253* {@code get("realms", "R", "kdc")} will return null if "R" is not254* defined in [realms] or "kdc" is not defined for "R".255* @throws IllegalArgumentException if any of the keys is illegal, either256* because a key not the last one is not a (sub)section name or the last257* key is still a section name. For example, {@code get("libdefaults")}258* throws this exception because [libdefaults] is a section name instead of259* a value name, and {@code get("libdefaults", "forwardable", "tail")}260* also throws this exception because "forwardable" is already a value name261* and has no sub-key at all (given "forwardable" is defined, otherwise,262* this method has no knowledge if it's a value name or a section name),263*/264public String get(String... keys) {265Vector<String> v = getString0(keys);266if (v == null) return null;267return v.lastElement();268}269270/**271* Gets the boolean value for the specified keys. Returns TRUE if the272* string value is "yes", or "true", FALSE if "no", or "false", or null273* if otherwise or not defined. The comparision is case-insensitive.274*275* @param keys the keys, see {@link #get(String...)}276* @return the boolean value, or null if there is no value defined or the277* value does not look like a boolean value.278* @throws IllegalArgumentException see {@link #get(String...)}279*/280public Boolean getBooleanObject(String... keys) {281String s = get(keys);282if (s == null) {283return null;284}285switch (s.toLowerCase(Locale.US)) {286case "yes": case "true":287return Boolean.TRUE;288case "no": case "false":289return Boolean.FALSE;290default:291return null;292}293}294295/**296* Gets all values (at least one) for the specified keys separated by297* a whitespace, or null if there is no such keys.298* The values can either be provided on a single line, or on multiple lines299* using the same key. When provided on a single line, the value can be300* comma or space separated.301* @throws IllegalArgumentException if any of the keys is illegal302* (See {@link #get})303*/304public String getAll(String... keys) {305Vector<String> v = getString0(keys);306if (v == null) return null;307StringBuilder sb = new StringBuilder();308boolean first = true;309for (String s: v) {310s = s.replaceAll("[\\s,]+", " ");311if (first) {312sb.append(s);313first = false;314} else {315sb.append(' ').append(s);316}317}318return sb.toString();319}320321/**322* Returns true if keys exists, can be either final string(s) or sub-stanza323* @throws IllegalArgumentException if any of the keys is illegal324* (See {@link #get})325*/326public boolean exists(String... keys) {327return get0(keys) != null;328}329330// Returns final string value(s) for given keys.331@SuppressWarnings("unchecked")332private Vector<String> getString0(String... keys) {333try {334return (Vector<String>)get0(keys);335} catch (ClassCastException cce) {336throw new IllegalArgumentException(cce);337}338}339340// Internal method. Returns the value for keys, which can be a sub-stanza341// or final string value(s).342// The only method (except for toString) that reads stanzaTable directly.343@SuppressWarnings("unchecked")344private Object get0(String... keys) {345Object current = stanzaTable;346try {347for (String key: keys) {348current = ((Hashtable<String,Object>)current).get(key);349if (current == null) return null;350}351return current;352} catch (ClassCastException cce) {353throw new IllegalArgumentException(cce);354}355}356357/**358* Translates a duration value into seconds.359*360* The format can be one of "h:m[:s]", "NdNhNmNs", and "N". See361* http://web.mit.edu/kerberos/krb5-devel/doc/basic/date_format.html#duration362* for definitions.363*364* @param s the string duration365* @return time in seconds366* @throws KrbException if format is illegal367*/368public static int duration(String s) throws KrbException {369370if (s.isEmpty()) {371throw new KrbException("Duration cannot be empty");372}373374// N375if (s.matches("\\d+")) {376return Integer.parseInt(s);377}378379// h:m[:s]380Matcher m = Pattern.compile("(\\d+):(\\d+)(:(\\d+))?").matcher(s);381if (m.matches()) {382int hr = Integer.parseInt(m.group(1));383int min = Integer.parseInt(m.group(2));384if (min >= 60) {385throw new KrbException("Illegal duration format " + s);386}387int result = hr * 3600 + min * 60;388if (m.group(4) != null) {389int sec = Integer.parseInt(m.group(4));390if (sec >= 60) {391throw new KrbException("Illegal duration format " + s);392}393result += sec;394}395return result;396}397398// NdNhNmNs399// 120m allowed. Maybe 1h120m is not good, but still allowed400m = Pattern.compile(401"((\\d+)d)?\\s*((\\d+)h)?\\s*((\\d+)m)?\\s*((\\d+)s)?",402Pattern.CASE_INSENSITIVE).matcher(s);403if (m.matches()) {404int result = 0;405if (m.group(2) != null) {406result += 86400 * Integer.parseInt(m.group(2));407}408if (m.group(4) != null) {409result += 3600 * Integer.parseInt(m.group(4));410}411if (m.group(6) != null) {412result += 60 * Integer.parseInt(m.group(6));413}414if (m.group(8) != null) {415result += Integer.parseInt(m.group(8));416}417return result;418}419420throw new KrbException("Illegal duration format " + s);421}422423/**424* Gets the int value for the specified keys.425* @param keys the keys426* @return the int value, Integer.MIN_VALUE is returned if it cannot be427* found or the value is not a legal integer.428* @throws IllegalArgumentException if any of the keys is illegal429* @see #get(java.lang.String[])430*/431public int getIntValue(String... keys) {432String result = get(keys);433int value = Integer.MIN_VALUE;434if (result != null) {435try {436value = parseIntValue(result);437} catch (NumberFormatException e) {438if (DEBUG) {439System.out.println("Exception in getting value of " +440Arrays.toString(keys) + " " +441e.getMessage());442System.out.println("Setting " + Arrays.toString(keys) +443" to minimum value");444}445value = Integer.MIN_VALUE;446}447}448return value;449}450451/**452* Gets the boolean value for the specified keys.453* @param keys the keys454* @return the boolean value, false is returned if it cannot be455* found or the value is not "true" (case insensitive).456* @throw IllegalArgumentException if any of the keys is illegal457* @see #get(java.lang.String[])458*/459public boolean getBooleanValue(String... keys) {460String val = get(keys);461if (val != null && val.equalsIgnoreCase("true")) {462return true;463} else {464return false;465}466}467468/**469* Parses a string to an integer. The convertible strings include the470* string representations of positive integers, negative integers, and471* hex decimal integers. Valid inputs are, e.g., -1234, +1234,472* 0x40000.473*474* @param input the String to be converted to an Integer.475* @return an numeric value represented by the string476* @exception NumberFormationException if the String does not contain a477* parsable integer.478*/479private int parseIntValue(String input) throws NumberFormatException {480int value = 0;481if (input.startsWith("+")) {482String temp = input.substring(1);483return Integer.parseInt(temp);484} else if (input.startsWith("0x")) {485String temp = input.substring(2);486char[] chars = temp.toCharArray();487if (chars.length > 8) {488throw new NumberFormatException();489} else {490for (int i = 0; i < chars.length; i++) {491int index = chars.length - i - 1;492switch (chars[i]) {493case '0':494value += 0;495break;496case '1':497value += 1 * getBase(index);498break;499case '2':500value += 2 * getBase(index);501break;502case '3':503value += 3 * getBase(index);504break;505case '4':506value += 4 * getBase(index);507break;508case '5':509value += 5 * getBase(index);510break;511case '6':512value += 6 * getBase(index);513break;514case '7':515value += 7 * getBase(index);516break;517case '8':518value += 8 * getBase(index);519break;520case '9':521value += 9 * getBase(index);522break;523case 'a':524case 'A':525value += 10 * getBase(index);526break;527case 'b':528case 'B':529value += 11 * getBase(index);530break;531case 'c':532case 'C':533value += 12 * getBase(index);534break;535case 'd':536case 'D':537value += 13 * getBase(index);538break;539case 'e':540case 'E':541value += 14 * getBase(index);542break;543case 'f':544case 'F':545value += 15 * getBase(index);546break;547default:548throw new NumberFormatException("Invalid numerical format");549}550}551}552if (value < 0) {553throw new NumberFormatException("Data overflow.");554}555} else {556value = Integer.parseInt(input);557}558return value;559}560561private int getBase(int i) {562int result = 16;563switch (i) {564case 0:565result = BASE16_0;566break;567case 1:568result = BASE16_1;569break;570case 2:571result = BASE16_2;572break;573case 3:574result = BASE16_3;575break;576default:577for (int j = 1; j < i; j++) {578result *= 16;579}580}581return result;582}583584/**585* Reads lines to the memory from the configuration file.586*587* Configuration file contains information about the default realm,588* ticket parameters, location of the KDC and the admin server for589* known realms, etc. The file is divided into sections. Each section590* contains one or more name/value pairs with one pair per line. A591* typical file would be:592* <pre>593* [libdefaults]594* default_realm = EXAMPLE.COM595* default_tgs_enctypes = des-cbc-md5596* default_tkt_enctypes = des-cbc-md5597* [realms]598* EXAMPLE.COM = {599* kdc = kerberos.example.com600* kdc = kerberos-1.example.com601* admin_server = kerberos.example.com602* }603* SAMPLE_COM = {604* kdc = orange.sample.com605* admin_server = orange.sample.com606* }607* [domain_realm]608* blue.sample.com = TEST.SAMPLE.COM609* .backup.com = EXAMPLE.COM610* </pre>611* @return an ordered list of strings representing the config file after612* some initial processing, including:<ol>613* <li> Comment lines and empty lines are removed614* <li> "{" not at the end of a line is appended to the previous line615* <li> The content of a section is also placed between "{" and "}".616* <li> Lines are trimmed</ol>617* @throws IOException if there is an I/O error618* @throws KrbException if there is a file format error619*/620private List<String> loadConfigFile(final String fileName)621throws IOException, KrbException {622try {623List<String> v = new ArrayList<>();624try (BufferedReader br = new BufferedReader(new InputStreamReader(625AccessController.doPrivileged(626new PrivilegedExceptionAction<FileInputStream> () {627public FileInputStream run() throws IOException {628return new FileInputStream(fileName);629}630})))) {631String line;632String previous = null;633while ((line = br.readLine()) != null) {634line = line.trim();635if (line.isEmpty() || line.startsWith("#") || line.startsWith(";")) {636// ignore comments and blank line637// Comments start with '#' or ';'638continue;639}640// In practice, a subsection might look like:641// [realms]642// EXAMPLE.COM =643// {644// kdc = kerberos.example.com645// ...646// }647// Before parsed into stanza table, it needs to be648// converted into a canonicalized style (no indent):649// realms = {650// EXAMPLE.COM = {651// kdc = kerberos.example.com652// ...653// }654// }655//656if (line.startsWith("[")) {657if (!line.endsWith("]")) {658throw new KrbException("Illegal config content:"659+ line);660}661if (previous != null) {662v.add(previous);663v.add("}");664}665String title = line.substring(6661, line.length()-1).trim();667if (title.isEmpty()) {668throw new KrbException("Illegal config content:"669+ line);670}671previous = title + " = {";672} else if (line.startsWith("{")) {673if (previous == null) {674throw new KrbException(675"Config file should not start with \"{\"");676}677previous += " {";678if (line.length() > 1) {679// { and content on the same line680v.add(previous);681previous = line.substring(1).trim();682}683} else {684// Lines before the first section are ignored685if (previous != null) {686v.add(previous);687previous = line;688}689}690}691if (previous != null) {692v.add(previous);693v.add("}");694}695}696return v;697} catch (java.security.PrivilegedActionException pe) {698throw (IOException)pe.getException();699}700}701702/**703* Parses stanza names and values from configuration file to704* stanzaTable (Hashtable). Hashtable key would be stanza names,705* (libdefaults, realms, domain_realms, etc), and the hashtable value706* would be another hashtable which contains the key-value pairs under707* a stanza name. The value of this sub-hashtable can be another hashtable708* containing another sub-sub-section or a vector of strings for709* final values (even if there is only one value defined).710* <p>711* For duplicates section names, the latter overwrites the former. For712* duplicate value names, the values are in a vector in its appearing order.713* </ol>714* Please note that this behavior is Java traditional. and it is715* not the same as the MIT krb5 behavior, where:<ol>716* <li>Duplicated root sections will be merged717* <li>For duplicated sub-sections, the former overwrites the latter718* <li>Duplicate keys for values are always saved in a vector719* </ol>720* @param v the strings in the file, never null, might be empty721* @throws KrbException if there is a file format error722*/723@SuppressWarnings("unchecked")724private Hashtable<String,Object> parseStanzaTable(List<String> v)725throws KrbException {726Hashtable<String,Object> current = stanzaTable;727for (String line: v) {728// There are 3 kinds of lines729// 1. a = b730// 2. a = {731// 3. }732if (line.equals("}")) {733// Go back to parent, see below734current = (Hashtable<String,Object>)current.remove(" PARENT ");735if (current == null) {736throw new KrbException("Unmatched close brace");737}738} else {739int pos = line.indexOf('=');740if (pos < 0) {741throw new KrbException("Illegal config content:" + line);742}743String key = line.substring(0, pos).trim();744String value = trimmed(line.substring(pos+1));745if (value.equals("{")) {746Hashtable<String,Object> subTable;747if (current == stanzaTable) {748key = key.toLowerCase(Locale.US);749}750subTable = new Hashtable<>();751current.put(key, subTable);752// A special entry for its parent. Put whitespaces around,753// so will never be confused with a normal key754subTable.put(" PARENT ", current);755current = subTable;756} else {757Vector<String> values;758if (current.containsKey(key)) {759Object obj = current.get(key);760// If a key first shows as a section and then a value,761// this is illegal. However, we haven't really forbid762// first value then section, which the final result763// is a section.764if (!(obj instanceof Vector)) {765throw new KrbException("Key " + key766+ "used for both value and section");767}768values = (Vector<String>)current.get(key);769} else {770values = new Vector<String>();771current.put(key, values);772}773values.add(value);774}775}776}777if (current != stanzaTable) {778throw new KrbException("Not closed");779}780return current;781}782783/**784* Gets the default Java configuration file name.785*786* If the system property "java.security.krb5.conf" is defined, we'll787* use its value, no matter if the file exists or not. Otherwise, we788* will look at $JAVA_HOME/lib/security directory with "krb5.conf" name,789* and return it if the file exists.790*791* The method returns null if it cannot find a Java config file.792*/793private String getJavaFileName() {794String name = getProperty("java.security.krb5.conf");795if (name == null) {796name = getProperty("java.home") + File.separator +797"lib" + File.separator + "security" +798File.separator + "krb5.conf";799if (!fileExists(name)) {800name = null;801}802}803if (DEBUG) {804System.out.println("Java config name: " + name);805}806return name;807}808809/**810* Gets the default native configuration file name.811*812* Depending on the OS type, the method returns the default native813* kerberos config file name, which is at windows directory with814* the name of "krb5.ini" for Windows, /etc/krb5/krb5.conf for Solaris,815* /etc/krb5.conf otherwise. Mac OSX X has a different file name.816*817* Note: When the Terminal Service is started in Windows (from 2003),818* there are two kinds of Windows directories: A system one (say,819* C:\Windows), and a user-private one (say, C:\Users\Me\Windows).820* We will first look for krb5.ini in the user-private one. If not821* found, try the system one instead.822*823* This method will always return a non-null non-empty file name,824* even if that file does not exist.825*/826private String getNativeFileName() {827String name = null;828String osname = getProperty("os.name");829if (osname.startsWith("Windows")) {830try {831Credentials.ensureLoaded();832} catch (Exception e) {833// ignore exceptions834}835if (Credentials.alreadyLoaded) {836String path = getWindowsDirectory(false);837if (path != null) {838if (path.endsWith("\\")) {839path = path + "krb5.ini";840} else {841path = path + "\\krb5.ini";842}843if (fileExists(path)) {844name = path;845}846}847if (name == null) {848path = getWindowsDirectory(true);849if (path != null) {850if (path.endsWith("\\")) {851path = path + "krb5.ini";852} else {853path = path + "\\krb5.ini";854}855name = path;856}857}858}859if (name == null) {860name = "c:\\winnt\\krb5.ini";861}862} else if (osname.startsWith("SunOS")) {863name = "/etc/krb5/krb5.conf";864} else if (osname.contains("OS X")) {865name = findMacosConfigFile();866} else {867name = "/etc/krb5.conf";868}869if (DEBUG) {870System.out.println("Native config name: " + name);871}872return name;873}874875private static String getProperty(String property) {876return java.security.AccessController.doPrivileged(877new sun.security.action.GetPropertyAction(property));878}879880private String findMacosConfigFile() {881String userHome = getProperty("user.home");882final String PREF_FILE = "/Library/Preferences/edu.mit.Kerberos";883String userPrefs = userHome + PREF_FILE;884885if (fileExists(userPrefs)) {886return userPrefs;887}888889if (fileExists(PREF_FILE)) {890return PREF_FILE;891}892893return "/etc/krb5.conf";894}895896private static String trimmed(String s) {897s = s.trim();898if (s.length() >= 2 &&899((s.charAt(0) == '"' && s.charAt(s.length()-1) == '"') ||900(s.charAt(0) == '\'' && s.charAt(s.length()-1) == '\''))) {901s = s.substring(1, s.length()-1).trim();902}903return s;904}905906/**907* For testing purpose. This method lists all information being parsed from908* the configuration file to the hashtable.909*/910public void listTable() {911System.out.println(this);912}913914/**915* Returns all etypes specified in krb5.conf for the given configName,916* or all the builtin defaults. This result is always non-empty.917* If no etypes are found, an exception is thrown.918*/919public int[] defaultEtype(String configName) throws KrbException {920String default_enctypes;921default_enctypes = get("libdefaults", configName);922int[] etype;923if (default_enctypes == null) {924if (DEBUG) {925System.out.println("Using builtin default etypes for " +926configName);927}928etype = EType.getBuiltInDefaults();929} else {930String delim = " ";931StringTokenizer st;932for (int j = 0; j < default_enctypes.length(); j++) {933if (default_enctypes.substring(j, j + 1).equals(",")) {934// only two delimiters are allowed to use935// according to Kerberos DCE doc.936delim = ",";937break;938}939}940st = new StringTokenizer(default_enctypes, delim);941int len = st.countTokens();942ArrayList<Integer> ls = new ArrayList<>(len);943int type;944for (int i = 0; i < len; i++) {945type = Config.getType(st.nextToken());946if (type != -1 && EType.isSupported(type)) {947ls.add(type);948}949}950if (ls.isEmpty()) {951throw new KrbException("no supported default etypes for "952+ configName);953} else {954etype = new int[ls.size()];955for (int i = 0; i < etype.length; i++) {956etype[i] = ls.get(i);957}958}959}960961if (DEBUG) {962System.out.print("default etypes for " + configName + ":");963for (int i = 0; i < etype.length; i++) {964System.out.print(" " + etype[i]);965}966System.out.println(".");967}968return etype;969}970971972/**973* Get the etype and checksum value for the specified encryption and974* checksum type.975*976*/977/*978* This method converts the string representation of encryption type and979* checksum type to int value that can be later used by EType and980* Checksum classes.981*/982public static int getType(String input) {983int result = -1;984if (input == null) {985return result;986}987if (input.startsWith("d") || (input.startsWith("D"))) {988if (input.equalsIgnoreCase("des-cbc-crc")) {989result = EncryptedData.ETYPE_DES_CBC_CRC;990} else if (input.equalsIgnoreCase("des-cbc-md5")) {991result = EncryptedData.ETYPE_DES_CBC_MD5;992} else if (input.equalsIgnoreCase("des-mac")) {993result = Checksum.CKSUMTYPE_DES_MAC;994} else if (input.equalsIgnoreCase("des-mac-k")) {995result = Checksum.CKSUMTYPE_DES_MAC_K;996} else if (input.equalsIgnoreCase("des-cbc-md4")) {997result = EncryptedData.ETYPE_DES_CBC_MD4;998} else if (input.equalsIgnoreCase("des3-cbc-sha1") ||999input.equalsIgnoreCase("des3-hmac-sha1") ||1000input.equalsIgnoreCase("des3-cbc-sha1-kd") ||1001input.equalsIgnoreCase("des3-cbc-hmac-sha1-kd")) {1002result = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD;1003}1004} else if (input.startsWith("a") || (input.startsWith("A"))) {1005// AES1006if (input.equalsIgnoreCase("aes128-cts") ||1007input.equalsIgnoreCase("aes128-cts-hmac-sha1-96")) {1008result = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96;1009} else if (input.equalsIgnoreCase("aes256-cts") ||1010input.equalsIgnoreCase("aes256-cts-hmac-sha1-96")) {1011result = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96;1012// ARCFOUR-HMAC1013} else if (input.equalsIgnoreCase("arcfour-hmac") ||1014input.equalsIgnoreCase("arcfour-hmac-md5")) {1015result = EncryptedData.ETYPE_ARCFOUR_HMAC;1016}1017// RC4-HMAC1018} else if (input.equalsIgnoreCase("rc4-hmac")) {1019result = EncryptedData.ETYPE_ARCFOUR_HMAC;1020} else if (input.equalsIgnoreCase("CRC32")) {1021result = Checksum.CKSUMTYPE_CRC32;1022} else if (input.startsWith("r") || (input.startsWith("R"))) {1023if (input.equalsIgnoreCase("rsa-md5")) {1024result = Checksum.CKSUMTYPE_RSA_MD5;1025} else if (input.equalsIgnoreCase("rsa-md5-des")) {1026result = Checksum.CKSUMTYPE_RSA_MD5_DES;1027}1028} else if (input.equalsIgnoreCase("hmac-sha1-des3-kd")) {1029result = Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD;1030} else if (input.equalsIgnoreCase("hmac-sha1-96-aes128")) {1031result = Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128;1032} else if (input.equalsIgnoreCase("hmac-sha1-96-aes256")) {1033result = Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256;1034} else if (input.equalsIgnoreCase("hmac-md5-rc4") ||1035input.equalsIgnoreCase("hmac-md5-arcfour") ||1036input.equalsIgnoreCase("hmac-md5-enc")) {1037result = Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR;1038} else if (input.equalsIgnoreCase("NULL")) {1039result = EncryptedData.ETYPE_NULL;1040}10411042return result;1043}10441045/**1046* Resets the default kdc realm.1047* We do not need to synchronize these methods since assignments are atomic1048*1049* This method was useless. Kept here in case some class still calls it.1050*/1051public void resetDefaultRealm(String realm) {1052if (DEBUG) {1053System.out.println(">>> Config try resetting default kdc " + realm);1054}1055}10561057/**1058* Check to use addresses in tickets1059* use addresses if "no_addresses" or "noaddresses" is set to false1060*/1061public boolean useAddresses() {1062boolean useAddr = false;1063// use addresses if "no_addresses" is set to false1064String value = get("libdefaults", "no_addresses");1065useAddr = (value != null && value.equalsIgnoreCase("false"));1066if (useAddr == false) {1067// use addresses if "noaddresses" is set to false1068value = get("libdefaults", "noaddresses");1069useAddr = (value != null && value.equalsIgnoreCase("false"));1070}1071return useAddr;1072}10731074/**1075* Check if need to use DNS to locate Kerberos services1076*/1077private boolean useDNS(String name, boolean defaultValue) {1078Boolean value = getBooleanObject("libdefaults", name);1079if (value != null) {1080return value.booleanValue();1081}1082value = getBooleanObject("libdefaults", "dns_fallback");1083if (value != null) {1084return value.booleanValue();1085}1086return defaultValue;1087}10881089/**1090* Check if need to use DNS to locate the KDC1091*/1092private boolean useDNS_KDC() {1093return useDNS("dns_lookup_kdc", true);1094}10951096/*1097* Check if need to use DNS to locate the Realm1098*/1099private boolean useDNS_Realm() {1100return useDNS("dns_lookup_realm", false);1101}11021103/**1104* Gets default realm.1105* @throws KrbException where no realm can be located1106* @return the default realm, always non null1107*/1108public String getDefaultRealm() throws KrbException {1109if (defaultRealm != null) {1110return defaultRealm;1111}1112Exception cause = null;1113String realm = get("libdefaults", "default_realm");1114if ((realm == null) && useDNS_Realm()) {1115// use DNS to locate Kerberos realm1116try {1117realm = getRealmFromDNS();1118} catch (KrbException ke) {1119cause = ke;1120}1121}1122if (realm == null) {1123realm = java.security.AccessController.doPrivileged(1124new java.security.PrivilegedAction<String>() {1125@Override1126public String run() {1127String osname = System.getProperty("os.name");1128if (osname.startsWith("Windows")) {1129return System.getenv("USERDNSDOMAIN");1130}1131return null;1132}1133});1134}1135if (realm == null) {1136KrbException ke = new KrbException("Cannot locate default realm");1137if (cause != null) {1138ke.initCause(cause);1139}1140throw ke;1141}1142return realm;1143}11441145/**1146* Returns a list of KDC's with each KDC separated by a space1147*1148* @param realm the realm for which the KDC list is desired1149* @throws KrbException if there's no way to find KDC for the realm1150* @return the list of KDCs separated by a space, always non null1151*/1152public String getKDCList(String realm) throws KrbException {1153if (realm == null) {1154realm = getDefaultRealm();1155}1156if (realm.equalsIgnoreCase(defaultRealm)) {1157return defaultKDC;1158}1159Exception cause = null;1160String kdcs = getAll("realms", realm, "kdc");1161if ((kdcs == null) && useDNS_KDC()) {1162// use DNS to locate KDC1163try {1164kdcs = getKDCFromDNS(realm);1165} catch (KrbException ke) {1166cause = ke;1167}1168}1169if (kdcs == null) {1170kdcs = java.security.AccessController.doPrivileged(1171new java.security.PrivilegedAction<String>() {1172@Override1173public String run() {1174String osname = System.getProperty("os.name");1175if (osname.startsWith("Windows")) {1176String logonServer = System.getenv("LOGONSERVER");1177if (logonServer != null1178&& logonServer.startsWith("\\\\")) {1179logonServer = logonServer.substring(2);1180}1181return logonServer;1182}1183return null;1184}1185});1186}1187if (kdcs == null) {1188if (defaultKDC != null) {1189return defaultKDC;1190}1191KrbException ke = new KrbException("Cannot locate KDC");1192if (cause != null) {1193ke.initCause(cause);1194}1195throw ke;1196}1197return kdcs;1198}11991200/**1201* Locate Kerberos realm using DNS1202*1203* @return the Kerberos realm1204*/1205private String getRealmFromDNS() throws KrbException {1206// use DNS to locate Kerberos realm1207String realm = null;1208String hostName = null;1209try {1210hostName = InetAddress.getLocalHost().getCanonicalHostName();1211} catch (UnknownHostException e) {1212KrbException ke = new KrbException(Krb5.KRB_ERR_GENERIC,1213"Unable to locate Kerberos realm: " + e.getMessage());1214ke.initCause(e);1215throw (ke);1216}1217// get the domain realm mapping from the configuration1218String mapRealm = PrincipalName.mapHostToRealm(hostName);1219if (mapRealm == null) {1220// No match. Try search and/or domain in /etc/resolv.conf1221List<String> srchlist = ResolverConfiguration.open().searchlist();1222for (String domain: srchlist) {1223realm = checkRealm(domain);1224if (realm != null) {1225break;1226}1227}1228} else {1229realm = checkRealm(mapRealm);1230}1231if (realm == null) {1232throw new KrbException(Krb5.KRB_ERR_GENERIC,1233"Unable to locate Kerberos realm");1234}1235return realm;1236}12371238/**1239* Check if the provided realm is the correct realm1240* @return the realm if correct, or null otherwise1241*/1242private static String checkRealm(String mapRealm) {1243if (DEBUG) {1244System.out.println("getRealmFromDNS: trying " + mapRealm);1245}1246String[] records = null;1247String newRealm = mapRealm;1248while ((records == null) && (newRealm != null)) {1249// locate DNS TXT record1250records = KrbServiceLocator.getKerberosService(newRealm);1251newRealm = Realm.parseRealmComponent(newRealm);1252// if no DNS TXT records found, try again using sub-realm1253}1254if (records != null) {1255for (int i = 0; i < records.length; i++) {1256if (records[i].equalsIgnoreCase(mapRealm)) {1257return records[i];1258}1259}1260}1261return null;1262}12631264/**1265* Locate KDC using DNS1266*1267* @param realm the realm for which the master KDC is desired1268* @return the KDC1269*/1270private String getKDCFromDNS(String realm) throws KrbException {1271// use DNS to locate KDC1272String kdcs = "";1273String[] srvs = null;1274// locate DNS SRV record using UDP1275if (DEBUG) {1276System.out.println("getKDCFromDNS using UDP");1277}1278srvs = KrbServiceLocator.getKerberosService(realm, "_udp");1279if (srvs == null) {1280// locate DNS SRV record using TCP1281if (DEBUG) {1282System.out.println("getKDCFromDNS using TCP");1283}1284srvs = KrbServiceLocator.getKerberosService(realm, "_tcp");1285}1286if (srvs == null) {1287// no DNS SRV records1288throw new KrbException(Krb5.KRB_ERR_GENERIC,1289"Unable to locate KDC for realm " + realm);1290}1291if (srvs.length == 0) {1292return null;1293}1294for (int i = 0; i < srvs.length; i++) {1295kdcs += srvs[i].trim() + " ";1296}1297kdcs = kdcs.trim();1298if (kdcs.equals("")) {1299return null;1300}1301return kdcs;1302}13031304private boolean fileExists(String name) {1305return java.security.AccessController.doPrivileged(1306new FileExistsAction(name));1307}13081309static class FileExistsAction1310implements java.security.PrivilegedAction<Boolean> {13111312private String fileName;13131314public FileExistsAction(String fileName) {1315this.fileName = fileName;1316}13171318public Boolean run() {1319return new File(fileName).exists();1320}1321}13221323// Shows the content of the Config object for debug purpose.1324//1325// {1326// libdefaults = {1327// default_realm = R1328// }1329// realms = {1330// R = {1331// kdc = [k1,k2]1332// }1333// }1334// }13351336@Override1337public String toString() {1338StringBuffer sb = new StringBuffer();1339toStringInternal("", stanzaTable, sb);1340return sb.toString();1341}1342private static void toStringInternal(String prefix, Object obj,1343StringBuffer sb) {1344if (obj instanceof String) {1345// A string value, just print it1346sb.append(obj).append('\n');1347} else if (obj instanceof Hashtable) {1348// A table, start a new sub-section...1349Hashtable<?, ?> tab = (Hashtable<?, ?>)obj;1350sb.append("{\n");1351for (Object o: tab.keySet()) {1352// ...indent, print "key = ", and1353sb.append(prefix).append(" ").append(o).append(" = ");1354// ...go recursively into value1355toStringInternal(prefix + " ", tab.get(o), sb);1356}1357sb.append(prefix).append("}\n");1358} else if (obj instanceof Vector) {1359// A vector of strings, print them inside [ and ]1360Vector<?> v = (Vector<?>)obj;1361sb.append("[");1362boolean first = true;1363for (Object o: v.toArray()) {1364if (!first) sb.append(",");1365sb.append(o);1366first = false;1367}1368sb.append("]\n");1369}1370}1371}137213731374