History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/krb5/Config.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/krb5/Config.java+1314−0
@@ -0,0 +1,1314 @@
1+/*
2+ * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+/*
27+ *
28+ * (C) Copyright IBM Corp. 1999 All Rights Reserved.
29+ * Copyright 1997 The Open Group Research Institute. All rights reserved.
30+ */
31+package sun.security.krb5;
32+
33+import java.io.File;
34+import java.io.FileInputStream;
35+import java.util.Hashtable;
36+import java.util.Vector;
37+import java.util.ArrayList;
38+import java.io.BufferedReader;
39+import java.io.InputStreamReader;
40+import java.io.IOException;
41+import java.util.Enumeration;
42+import java.util.StringTokenizer;
43+import java.net.InetAddress;
44+import java.net.UnknownHostException;
45+import java.util.List;
46+import sun.net.dns.ResolverConfiguration;
47+import sun.security.krb5.internal.crypto.EType;
48+import sun.security.krb5.internal.ktab.*;
49+import sun.security.krb5.internal.Krb5;
50+
51+/**
52+ * This class maintains key-value pairs of Kerberos configurable constants
53+ * from configuration file or from user specified system properties.
54+ */
55+
56+public class Config {
57+
58+ /*
59+ * Only allow a single instance of Config.
60+ */
61+ private static Config singleton = null;
62+
63+ /*
64+ * Hashtable used to store configuration infomation.
65+ */
66+ private Hashtable<String,Object> stanzaTable;
67+
68+ private static boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
69+
70+ // these are used for hexdecimal calculation.
71+ private static final int BASE16_0 = 1;
72+ private static final int BASE16_1 = 16;
73+ private static final int BASE16_2 = 16 * 16;
74+ private static final int BASE16_3 = 16 * 16 * 16;
75+
76+ /**
77+ * Specified by system properties. Must be both null or non-null.
78+ */
79+ private final String defaultRealm;
80+ private final String defaultKDC;
81+
82+ // used for native interface
83+ private static native String getWindowsDirectory(boolean isSystem);
84+
85+
86+ /**
87+ * Gets an instance of Config class. One and only one instance (the
88+ * singleton) is returned.
89+ *
90+ * @exception KrbException if error occurs when constructing a Config
91+ * instance. Possible causes would be either of java.security.krb5.realm or
92+ * java.security.krb5.kdc not specified, error reading configuration file.
93+ */
94+ public static synchronized Config getInstance() throws KrbException {
95+ if (singleton == null) {
96+ singleton = new Config();
97+ }
98+ return singleton;
99+ }
100+
101+ /**
102+ * Refresh and reload the Configuration. This could involve,
103+ * for example reading the Configuration file again or getting
104+ * the java.security.krb5.* system properties again.
105+ *
106+ * @exception KrbException if error occurs when constructing a Config
107+ * instance. Possible causes would be either of java.security.krb5.realm or
108+ * java.security.krb5.kdc not specified, error reading configuration file.
109+ */
110+
111+ public static synchronized void refresh() throws KrbException {
112+ singleton = new Config();
113+ KdcComm.initStatic();
114+ }
115+
116+
117+ /**
118+ * Private constructor - can not be instantiated externally.
119+ */
120+ private Config() throws KrbException {
121+ /*
122+ * If either one system property is specified, we throw exception.
123+ */
124+ String tmp =
125+ java.security.AccessController.doPrivileged(
126+ new sun.security.action.GetPropertyAction
127+ ("java.security.krb5.kdc"));
128+ if (tmp != null) {
129+ // The user can specify a list of kdc hosts separated by ":"
130+ defaultKDC = tmp.replace(':', ' ');
131+ } else {
132+ defaultKDC = null;
133+ }
134+ defaultRealm =
135+ java.security.AccessController.doPrivileged(
136+ new sun.security.action.GetPropertyAction
137+ ("java.security.krb5.realm"));
138+ if ((defaultKDC == null && defaultRealm != null) ||
139+ (defaultRealm == null && defaultKDC != null)) {
140+ throw new KrbException
141+ ("System property java.security.krb5.kdc and " +
142+ "java.security.krb5.realm both must be set or " +
143+ "neither must be set.");
144+ }
145+
146+ // Always read the Kerberos configuration file
147+ try {
148+ Vector<String> configFile;
149+ configFile = loadConfigFile();
150+ stanzaTable = parseStanzaTable(configFile);
151+ } catch (IOException ioe) {
152+ // No krb5.conf, no problem. We'll use DNS or system property etc.
153+ }
154+ }
155+
156+ /**
157+ * Gets the default int value for the specified name.
158+ * @param name the name.
159+ * @return the default Integer, null is returned if no such name and
160+ * value are found in configuration file, or error occurs when parsing
161+ * string to integer.
162+ */
163+ public int getDefaultIntValue(String name) {
164+ String result = null;
165+ int value = Integer.MIN_VALUE;
166+ result = getDefault(name);
167+ if (result != null) {
168+ try {
169+ value = parseIntValue(result);
170+ } catch (NumberFormatException e) {
171+ if (DEBUG) {
172+ System.out.println("Exception in getting value of " +
173+ name + " " +
174+ e.getMessage());
175+ System.out.println("Setting " + name +
176+ " to minimum value");
177+ }
178+ value = Integer.MIN_VALUE;
179+ }
180+ }
181+ return value;
182+ }
183+
184+ /**
185+ * Gets the default int value for the specified name in the specified
186+ * section. <br>This method is quicker by using section name as the
187+ * search key.
188+ * @param name the name.
189+ * @param sectio the name string of the section.
190+ * @return the default Integer, null is returned if no such name and
191+ * value are found in configuration file, or error occurs when parsing
192+ * string to integer.
193+ */
194+ public int getDefaultIntValue(String name, String section) {
195+ String result = null;
196+ int value = Integer.MIN_VALUE;
197+ result = getDefault(name, section);
198+ if (result != null) {
199+ try {
200+ value = parseIntValue(result);
201+ } catch (NumberFormatException e) {
202+ if (DEBUG) {
203+ System.out.println("Exception in getting value of " +
204+ name +" in section " +
205+ section + " " + e.getMessage());
206+ System.out.println("Setting " + name +
207+ " to minimum value");
208+ }
209+ value = Integer.MIN_VALUE;
210+ }
211+ }
212+ return value;
213+ }
214+
215+ /**
216+ * Gets the default string value for the specified name.
217+ * @param name the name.
218+ * @return the default value, null is returned if it cannot be found.
219+ */
220+ public String getDefault(String name) {
221+ if (stanzaTable == null) {
222+ return null;
223+ } else {
224+ return getDefault(name, stanzaTable);
225+ }
226+ }
227+
228+ /**
229+ * This method does the real job to recursively search through the
230+ * stanzaTable.
231+ * @param k the key string.
232+ * @param t stanzaTable or sub hashtable within it.
233+ * @return the value found in config file, returns null if no value
234+ * matched with the key is found.
235+ */
236+ private String getDefault(String k, Hashtable t) {
237+ String result = null;
238+ String key;
239+ if (stanzaTable != null) {
240+ for (Enumeration e = t.keys(); e.hasMoreElements(); ) {
241+ key = (String)e.nextElement();
242+ Object ob = t.get(key);
243+ if (ob instanceof Hashtable) {
244+ result = getDefault(k, (Hashtable)ob);
245+ if (result != null) {
246+ return result;
247+ }
248+ } else if (key.equalsIgnoreCase(k)) {
249+ if (ob instanceof String) {
250+ return (String)(t.get(key));
251+ } else if (ob instanceof Vector) {
252+ result = "";
253+ int length = ((Vector)ob).size();
254+ for (int i = 0; i < length; i++) {
255+ if (i == length -1) {
256+ result +=
257+ (String)(((Vector)ob).elementAt(i));
258+ } else {
259+ result +=
260+ (String)(((Vector)ob).elementAt(i)) + " ";
261+ }
262+ }
263+ return result;
264+ }
265+ }
266+ }
267+ }
268+ return result;
269+ }
270+
271+ /**
272+ * Gets the default string value for the specified name in the
273+ * specified section.
274+ * <br>This method is quicker by using the section name as the search key.
275+ * @param name the name.
276+ * @param section the name of the section.
277+ * @return the default value, null is returned if it cannot be found.
278+ */
279+ public String getDefault(String name, String section) {
280+ String stanzaName;
281+ String result = null;
282+ Hashtable subTable;
283+
284+ if (stanzaTable != null) {
285+ for (Enumeration e = stanzaTable.keys(); e.hasMoreElements(); ) {
286+ stanzaName = (String)e.nextElement();
287+ subTable = (Hashtable)stanzaTable.get(stanzaName);
288+ if (stanzaName.equalsIgnoreCase(section)) {
289+ if (subTable.containsKey(name)) {
290+ return (String)(subTable.get(name));
291+ }
292+ } else if (subTable.containsKey(section)) {
293+ Object ob = subTable.get(section);
294+ if (ob instanceof Hashtable) {
295+ Hashtable temp = (Hashtable)ob;
296+ if (temp.containsKey(name)) {
297+ Object object = temp.get(name);
298+ if (object instanceof Vector) {
299+ result = "";
300+ int length = ((Vector)object).size();
301+ for (int i = 0; i < length; i++) {
302+ if (i == length - 1) {
303+ result +=
304+ (String)(((Vector)object).elementAt(i));
305+ } else {
306+ result +=
307+ (String)(((Vector)object).elementAt(i))
308+ + " ";
309+ }
310+ }
311+ } else {
312+ result = (String)object;
313+ }
314+ }
315+ }
316+ }
317+ }
318+ }
319+ return result;
320+ }
321+
322+ /**
323+ * Gets the default boolean value for the specified name.
324+ * @param name the name.
325+ * @return the default boolean value, false is returned if it cannot be
326+ * found.
327+ */
328+ public boolean getDefaultBooleanValue(String name) {
329+ String val = null;
330+ if (stanzaTable == null) {
331+ val = null;
332+ } else {
333+ val = getDefault(name, stanzaTable);
334+ }
335+ if (val != null && val.equalsIgnoreCase("true")) {
336+ return true;
337+ } else {
338+ return false;
339+ }
340+ }
341+
342+ /**
343+ * Gets the default boolean value for the specified name in the
344+ * specified section.
345+ * <br>This method is quicker by using the section name as the search key.
346+ * @param name the name.
347+ * @param section the name of the section.
348+ * @return the default boolean value, false is returned if it cannot be
349+ * found.
350+ */
351+ public boolean getDefaultBooleanValue(String name, String section) {
352+ String val = getDefault(name, section);
353+ if (val != null && val.equalsIgnoreCase("true")) {
354+ return true;
355+ } else {
356+ return false;
357+ }
358+ }
359+
360+ /**
361+ * Parses a string to an integer. The convertible strings include the
362+ * string representations of positive integers, negative integers, and
363+ * hex decimal integers. Valid inputs are, e.g., -1234, +1234,
364+ * 0x40000.
365+ *
366+ * @param input the String to be converted to an Integer.
367+ * @return an numeric value represented by the string
368+ * @exception NumberFormationException if the String does not contain a
369+ * parsable integer.
370+ */
371+ private int parseIntValue(String input) throws NumberFormatException {
372+ int value = 0;
373+ if (input.startsWith("+")) {
374+ String temp = input.substring(1);
375+ return Integer.parseInt(temp);
376+ } else if (input.startsWith("0x")) {
377+ String temp = input.substring(2);
378+ char[] chars = temp.toCharArray();
379+ if (chars.length > 8) {
380+ throw new NumberFormatException();
381+ } else {
382+ for (int i = 0; i < chars.length; i++) {
383+ int index = chars.length - i - 1;
384+ switch (chars[i]) {
385+ case '0':
386+ value += 0;
387+ break;
388+ case '1':
389+ value += 1 * getBase(index);
390+ break;
391+ case '2':
392+ value += 2 * getBase(index);
393+ break;
394+ case '3':
395+ value += 3 * getBase(index);
396+ break;
397+ case '4':
398+ value += 4 * getBase(index);
399+ break;
400+ case '5':
401+ value += 5 * getBase(index);
402+ break;
403+ case '6':
404+ value += 6 * getBase(index);
405+ break;
406+ case '7':
407+ value += 7 * getBase(index);
408+ break;
409+ case '8':
410+ value += 8 * getBase(index);
411+ break;
412+ case '9':
413+ value += 9 * getBase(index);
414+ break;
415+ case 'a':
416+ case 'A':
417+ value += 10 * getBase(index);
418+ break;
419+ case 'b':
420+ case 'B':
421+ value += 11 * getBase(index);
422+ break;
423+ case 'c':
424+ case 'C':
425+ value += 12 * getBase(index);
426+ break;
427+ case 'd':
428+ case 'D':
429+ value += 13 * getBase(index);
430+ break;
431+ case 'e':
432+ case 'E':
433+ value += 14 * getBase(index);
434+ break;
435+ case 'f':
436+ case 'F':
437+ value += 15 * getBase(index);
438+ break;
439+ default:
440+ throw new NumberFormatException("Invalid numerical format");
441+ }
442+ }
443+ }
444+ if (value < 0) {
445+ throw new NumberFormatException("Data overflow.");
446+ }
447+ } else {
448+ value = Integer.parseInt(input);
449+ }
450+ return value;
451+ }
452+
453+ private int getBase(int i) {
454+ int result = 16;
455+ switch (i) {
456+ case 0:
457+ result = BASE16_0;
458+ break;
459+ case 1:
460+ result = BASE16_1;
461+ break;
462+ case 2:
463+ result = BASE16_2;
464+ break;
465+ case 3:
466+ result = BASE16_3;
467+ break;
468+ default:
469+ for (int j = 1; j < i; j++) {
470+ result *= 16;
471+ }
472+ }
473+ return result;
474+ }
475+
476+ /**
477+ * Finds the matching value in the hashtable.
478+ */
479+ private String find(String key1, String key2) {
480+ String result;
481+ if ((stanzaTable != null) &&
482+ ((result = (String)
483+ (((Hashtable)(stanzaTable.get(key1))).get(key2))) != null)) {
484+ return result;
485+ } else {
486+ return "";
487+ }
488+ }
489+
490+ /**
491+ * Reads name/value pairs to the memory from the configuration
492+ * file. The default location of the configuration file is in java home
493+ * directory.
494+ *
495+ * Configuration file contains information about the default realm,
496+ * ticket parameters, location of the KDC and the admin server for
497+ * known realms, etc. The file is divided into sections. Each section
498+ * contains one or more name/value pairs with one pair per line. A
499+ * typical file would be:
500+ * [libdefaults]
501+ * default_realm = EXAMPLE.COM
502+ * default_tgs_enctypes = des-cbc-md5
503+ * default_tkt_enctypes = des-cbc-md5
504+ * [realms]
505+ * EXAMPLE.COM = {
506+ * kdc = kerberos.example.com
507+ * kdc = kerberos-1.example.com
508+ * admin_server = kerberos.example.com
509+ * }
510+ * SAMPLE_COM = {
511+ * kdc = orange.sample.com
512+ * admin_server = orange.sample.com
513+ * }
514+ * [domain_realm]
515+ * blue.sample.com = TEST.SAMPLE.COM
516+ * .backup.com = EXAMPLE.COM
517+ */
518+ private Vector<String> loadConfigFile() throws IOException {
519+ try {
520+ final String fileName = getFileName();
521+ if (!fileName.equals("")) {
522+ BufferedReader br = new BufferedReader(new InputStreamReader(
523+ java.security.AccessController.doPrivileged(
524+ new java.security.PrivilegedExceptionAction<FileInputStream> () {
525+ public FileInputStream run() throws IOException {
526+ return new FileInputStream(fileName);
527+ }
528+ })));
529+ String Line;
530+ Vector<String> v = new Vector<>();
531+ String previous = null;
532+ while ((Line = br.readLine()) != null) {
533+ // ignore comments and blank line in the configuration file.
534+ // Comments start with #.
535+ if (!(Line.startsWith("#") || Line.trim().isEmpty())) {
536+ String current = Line.trim();
537+ // In practice, a subsection might look like:
538+ // EXAMPLE.COM =
539+ // {
540+ // kdc = kerberos.example.com
541+ // ...
542+ // }
543+ // Before parsed into stanza table, it needs to be
544+ // converted into formal style:
545+ // EXAMPLE.COM = {
546+ // kdc = kerberos.example.com
547+ // ...
548+ // }
549+ //
550+ // So, if a line is "{", adhere to the previous line.
551+ if (current.equals("{")) {
552+ if (previous == null) {
553+ throw new IOException(
554+ "Config file should not start with \"{\"");
555+ }
556+ previous += " " + current;
557+ } else {
558+ if (previous != null) {
559+ v.addElement(previous);
560+ }
561+ previous = current;
562+ }
563+ }
564+ }
565+ if (previous != null) {
566+ v.addElement(previous);
567+ }
568+
569+ br.close();
570+ return v;
571+ }
572+ return null;
573+ } catch (java.security.PrivilegedActionException pe) {
574+ throw (IOException)pe.getException();
575+ }
576+ }
577+
578+
579+ /**
580+ * Parses stanza names and values from configuration file to
581+ * stanzaTable (Hashtable). Hashtable key would be stanza names,
582+ * (libdefaults, realms, domain_realms, etc), and the hashtable value
583+ * would be another hashtable which contains the key-value pairs under
584+ * a stanza name.
585+ */
586+ private Hashtable<String,Object> parseStanzaTable(Vector<String> v) throws KrbException {
587+ if (v == null) {
588+ throw new KrbException("I/O error while reading" +
589+ " configuration file.");
590+ }
591+ Hashtable<String,Object> table = new Hashtable<>();
592+ for (int i = 0; i < v.size(); i++) {
593+ String line = v.elementAt(i).trim();
594+ if (line.equalsIgnoreCase("[realms]")) {
595+ for (int count = i + 1; count < v.size() + 1; count++) {
596+ // find the next stanza name
597+ if ((count == v.size()) ||
598+ (v.elementAt(count).startsWith("["))) {
599+ Hashtable<String,Hashtable<String,Vector<String>>> temp =
600+ new Hashtable<>();
601+ temp = parseRealmField(v, i + 1, count);
602+ table.put("realms", temp);
603+ i = count - 1;
604+ break;
605+ }
606+ }
607+ } else if (line.equalsIgnoreCase("[capaths]")) {
608+ for (int count = i + 1; count < v.size() + 1; count++) {
609+ // find the next stanza name
610+ if ((count == v.size()) ||
611+ (v.elementAt(count).startsWith("["))) {
612+ Hashtable<String,Hashtable<String,Vector<String>>> temp =
613+ new Hashtable<>();
614+ temp = parseRealmField(v, i + 1, count);
615+ table.put("capaths", temp);
616+ i = count - 1;
617+ break;
618+ }
619+ }
620+ } else if (line.startsWith("[") && line.endsWith("]")) {
621+ String key = line.substring(1, line.length() - 1);
622+ for (int count = i + 1; count < v.size() + 1; count++) {
623+ // find the next stanza name
624+ if ((count == v.size()) ||
625+ (v.elementAt(count).startsWith("["))) {
626+ Hashtable<String,String> temp =
627+ parseField(v, i + 1, count);
628+ table.put(key, temp);
629+ i = count - 1;
630+ break;
631+ }
632+ }
633+ }
634+ }
635+ return table;
636+ }
637+
638+ /**
639+ * Gets the default configuration file name. This method will never
640+ * return null.
641+ *
642+ * If the system property "java.security.krb5.conf" is defined, we'll
643+ * use its value, no matter if the file exists or not. Otherwise,
644+ * the file will be searched in a list of possible loations in the
645+ * following order:
646+ *
647+ * 1. at Java home lib\security directory with "krb5.conf" name,
648+ * 2. at windows directory with the name of "krb5.ini" for Windows,
649+ * /etc/krb5/krb5.conf for Solaris, /etc/krb5.conf otherwise.
650+ *
651+ * Note: When the Terminal Service is started in Windows (from 2003),
652+ * there are two kinds of Windows directories: A system one (say,
653+ * C:\Windows), and a user-private one (say, C:\Users\Me\Windows).
654+ * We will first look for krb5.ini in the user-private one. If not
655+ * found, try the system one instead.
656+ */
657+ private String getFileName() {
658+ String name =
659+ java.security.AccessController.doPrivileged(
660+ new sun.security.action.
661+ GetPropertyAction("java.security.krb5.conf"));
662+ if (name == null) {
663+ name = java.security.AccessController.doPrivileged(
664+ new sun.security.action.
665+ GetPropertyAction("java.home")) + File.separator +
666+ "lib" + File.separator + "security" +
667+ File.separator + "krb5.conf";
668+ if (!fileExists(name)) {
669+ name = null;
670+ String osname =
671+ java.security.AccessController.doPrivileged(
672+ new sun.security.action.GetPropertyAction("os.name"));
673+ if (osname.startsWith("Windows")) {
674+ try {
675+ Credentials.ensureLoaded();
676+ } catch (Exception e) {
677+ // ignore exceptions
678+ }
679+ if (Credentials.alreadyLoaded) {
680+ String path = getWindowsDirectory(false);
681+ if (path != null) {
682+ if (path.endsWith("\\")) {
683+ path = path + "krb5.ini";
684+ } else {
685+ path = path + "\\krb5.ini";
686+ }
687+ if (fileExists(path)) {
688+ name = path;
689+ }
690+ }
691+ if (name == null) {
692+ path = getWindowsDirectory(true);
693+ if (path != null) {
694+ if (path.endsWith("\\")) {
695+ path = path + "krb5.ini";
696+ } else {
697+ path = path + "\\krb5.ini";
698+ }
699+ name = path;
700+ }
701+ }
702+ }
703+ if (name == null) {
704+ name = "c:\\winnt\\krb5.ini";
705+ }
706+ } else if (osname.startsWith("SunOS")) {
707+ name = "/etc/krb5/krb5.conf";
708+ } else {
709+ name = "/etc/krb5.conf";
710+ }
711+ }
712+ }
713+ if (DEBUG) {
714+ System.out.println("Config name: " + name);
715+ }
716+ return name;
717+ }
718+
719+ private static String trimmed(String s) {
720+ s = s.trim();
721+ if (s.charAt(0) == '"' && s.charAt(s.length()-1) == '"' ||
722+ s.charAt(0) == '\'' && s.charAt(s.length()-1) == '\'') {
723+ s = s.substring(1, s.length()-1).trim();
724+ }
725+ return s;
726+ }
727+ /**
728+ * Parses key-value pairs under a stanza name.
729+ */
730+ private Hashtable<String,String> parseField(Vector<String> v, int start, int end) {
731+ Hashtable<String,String> table = new Hashtable<>();
732+ String line;
733+ for (int i = start; i < end; i++) {
734+ line = v.elementAt(i);
735+ for (int j = 0; j < line.length(); j++) {
736+ if (line.charAt(j) == '=') {
737+ String key = (line.substring(0, j)).trim();
738+ String value = trimmed(line.substring(j + 1));
739+ table.put(key, value);
740+ break;
741+ }
742+ }
743+ }
744+ return table;
745+ }
746+
747+ /**
748+ * Parses key-value pairs under [realms]. The key would be the realm
749+ * name, the value would be another hashtable which contains
750+ * information for the realm given within a pair of braces.
751+ */
752+ private Hashtable<String,Hashtable<String,Vector<String>>> parseRealmField(Vector<String> v, int start, int end) {
753+ Hashtable<String,Hashtable<String,Vector<String>>> table = new Hashtable<>();
754+ String line;
755+ for (int i = start; i < end; i++) {
756+ line = v.elementAt(i).trim();
757+ if (line.endsWith("{")) {
758+ String key = "";
759+ for (int j = 0; j < line.length(); j++) {
760+ if (line.charAt(j) == '=') {
761+ key = line.substring(0, j).trim();
762+ // get the key
763+ break;
764+ }
765+ }
766+ for (int k = i + 1; k < end; k++) {
767+ boolean found = false;
768+ line = v.elementAt(k).trim();
769+ for (int l = 0; l < line.length(); l++) {
770+ if (line.charAt(l) == '}') {
771+ found = true;
772+ break;
773+ }
774+ }
775+ if (found == true) {
776+ Hashtable<String,Vector<String>> temp = parseRealmFieldEx(v, i + 1, k);
777+ table.put(key, temp);
778+ i = k;
779+ found = false;
780+ break;
781+ }
782+
783+ }
784+ }
785+ }
786+ return table;
787+ }
788+
789+ /**
790+ * Parses key-value pairs within each braces under [realms].
791+ */
792+ private Hashtable<String,Vector<String>> parseRealmFieldEx(Vector<String> v, int start, int end) {
793+ Hashtable<String,Vector<String>> table = new Hashtable<>();
794+ Vector<String> keyVector = new Vector<>();
795+ Vector<String> nameVector = new Vector<>();
796+ String line = "";
797+ String key;
798+ for (int i = start; i < end; i++) {
799+ line = v.elementAt(i);
800+ for (int j = 0; j < line.length(); j++) {
801+ if (line.charAt(j) == '=') {
802+ int index;
803+ key = line.substring(0, j).trim();
804+ if (! exists(key, keyVector)) {
805+ keyVector.addElement(key);
806+ nameVector = new Vector<String> ();
807+ } else {
808+ nameVector = table.get(key);
809+ }
810+ nameVector.addElement(trimmed(line.substring(j + 1)));
811+ table.put(key, nameVector);
812+ break;
813+ }
814+ }
815+ }
816+ return table;
817+ }
818+
819+ /**
820+ * Compares the key with the known keys to see if it exists.
821+ */
822+ private boolean exists(String key, Vector v) {
823+ boolean exists = false;
824+ for (int i = 0; i < v.size(); i++) {
825+ if (((String)(v.elementAt(i))).equals(key)) {
826+ exists = true;
827+ }
828+ }
829+ return exists;
830+ }
831+
832+ /**
833+ * For testing purpose. This method lists all information being parsed from
834+ * the configuration file to the hashtable.
835+ */
836+ public void listTable() {
837+ listTable(stanzaTable);
838+ }
839+
840+ private void listTable(Hashtable table) {
841+ Vector v = new Vector();
842+ String key;
843+ if (stanzaTable != null) {
844+ for (Enumeration e = table.keys(); e.hasMoreElements(); ) {
845+ key = (String)e.nextElement();
846+ Object object = table.get(key);
847+ if (table == stanzaTable) {
848+ System.out.println("[" + key + "]");
849+ }
850+ if (object instanceof Hashtable) {
851+ if (table != stanzaTable)
852+ System.out.println("\t" + key + " = {");
853+ listTable((Hashtable)object);
854+ if (table != stanzaTable)
855+ System.out.println("\t}");
856+
857+ } else if (object instanceof String) {
858+ System.out.println("\t" + key + " = " +
859+ (String)table.get(key));
860+ } else if (object instanceof Vector) {
861+ v = (Vector)object;
862+ for (int i = 0; i < v.size(); i++) {
863+ System.out.println("\t" + key + " = " +
864+ (String)v.elementAt(i));
865+ }
866+ }
867+ }
868+ } else {
869+ System.out.println("Configuration file not found.");
870+ }
871+ }
872+
873+ /**
874+ * Returns the default encryption types.
875+ *
876+ */
877+ public int[] defaultEtype(String enctypes) {
878+ String default_enctypes;
879+ default_enctypes = getDefault(enctypes, "libdefaults");
880+ String delim = " ";
881+ StringTokenizer st;
882+ int[] etype;
883+ if (default_enctypes == null) {
884+ if (DEBUG) {
885+ System.out.println("Using builtin default etypes for " +
886+ enctypes);
887+ }
888+ etype = EType.getBuiltInDefaults();
889+ } else {
890+ for (int j = 0; j < default_enctypes.length(); j++) {
891+ if (default_enctypes.substring(j, j + 1).equals(",")) {
892+ // only two delimiters are allowed to use
893+ // according to Kerberos DCE doc.
894+ delim = ",";
895+ break;
896+ }
897+ }
898+ st = new StringTokenizer(default_enctypes, delim);
899+ int len = st.countTokens();
900+ ArrayList<Integer> ls = new ArrayList<>(len);
901+ int type;
902+ for (int i = 0; i < len; i++) {
903+ type = getType(st.nextToken());
904+ if ((type != -1) &&
905+ (EType.isSupported(type))) {
906+ ls.add(type);
907+ }
908+ }
909+ if (ls.size() == 0) {
910+ if (DEBUG) {
911+ System.out.println(
912+ "no supported default etypes for " + enctypes);
913+ }
914+ return null;
915+ } else {
916+ etype = new int[ls.size()];
917+ for (int i = 0; i < etype.length; i++) {
918+ etype[i] = ls.get(i);
919+ }
920+ }
921+ }
922+
923+ if (DEBUG) {
924+ System.out.print("default etypes for " + enctypes + ":");
925+ for (int i = 0; i < etype.length; i++) {
926+ System.out.print(" " + etype[i]);
927+ }
928+ System.out.println(".");
929+ }
930+ return etype;
931+ }
932+
933+
934+ /**
935+ * Get the etype and checksum value for the specified encryption and
936+ * checksum type.
937+ *
938+ */
939+ /*
940+ * This method converts the string representation of encryption type and
941+ * checksum type to int value that can be later used by EType and
942+ * Checksum classes.
943+ */
944+ public int getType(String input) {
945+ int result = -1;
946+ if (input == null) {
947+ return result;
948+ }
949+ if (input.startsWith("d") || (input.startsWith("D"))) {
950+ if (input.equalsIgnoreCase("des-cbc-crc")) {
951+ result = EncryptedData.ETYPE_DES_CBC_CRC;
952+ } else if (input.equalsIgnoreCase("des-cbc-md5")) {
953+ result = EncryptedData.ETYPE_DES_CBC_MD5;
954+ } else if (input.equalsIgnoreCase("des-mac")) {
955+ result = Checksum.CKSUMTYPE_DES_MAC;
956+ } else if (input.equalsIgnoreCase("des-mac-k")) {
957+ result = Checksum.CKSUMTYPE_DES_MAC_K;
958+ } else if (input.equalsIgnoreCase("des-cbc-md4")) {
959+ result = EncryptedData.ETYPE_DES_CBC_MD4;
960+ } else if (input.equalsIgnoreCase("des3-cbc-sha1") ||
961+ input.equalsIgnoreCase("des3-hmac-sha1") ||
962+ input.equalsIgnoreCase("des3-cbc-sha1-kd") ||
963+ input.equalsIgnoreCase("des3-cbc-hmac-sha1-kd")) {
964+ result = EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD;
965+ }
966+ } else if (input.startsWith("a") || (input.startsWith("A"))) {
967+ // AES
968+ if (input.equalsIgnoreCase("aes128-cts") ||
969+ input.equalsIgnoreCase("aes128-cts-hmac-sha1-96")) {
970+ result = EncryptedData.ETYPE_AES128_CTS_HMAC_SHA1_96;
971+ } else if (input.equalsIgnoreCase("aes256-cts") ||
972+ input.equalsIgnoreCase("aes256-cts-hmac-sha1-96")) {
973+ result = EncryptedData.ETYPE_AES256_CTS_HMAC_SHA1_96;
974+ // ARCFOUR-HMAC
975+ } else if (input.equalsIgnoreCase("arcfour-hmac") ||
976+ input.equalsIgnoreCase("arcfour-hmac-md5")) {
977+ result = EncryptedData.ETYPE_ARCFOUR_HMAC;
978+ }
979+ // RC4-HMAC
980+ } else if (input.equalsIgnoreCase("rc4-hmac")) {
981+ result = EncryptedData.ETYPE_ARCFOUR_HMAC;
982+ } else if (input.equalsIgnoreCase("CRC32")) {
983+ result = Checksum.CKSUMTYPE_CRC32;
984+ } else if (input.startsWith("r") || (input.startsWith("R"))) {
985+ if (input.equalsIgnoreCase("rsa-md5")) {
986+ result = Checksum.CKSUMTYPE_RSA_MD5;
987+ } else if (input.equalsIgnoreCase("rsa-md5-des")) {
988+ result = Checksum.CKSUMTYPE_RSA_MD5_DES;
989+ }
990+ } else if (input.equalsIgnoreCase("hmac-sha1-des3-kd")) {
991+ result = Checksum.CKSUMTYPE_HMAC_SHA1_DES3_KD;
992+ } else if (input.equalsIgnoreCase("hmac-sha1-96-aes128")) {
993+ result = Checksum.CKSUMTYPE_HMAC_SHA1_96_AES128;
994+ } else if (input.equalsIgnoreCase("hmac-sha1-96-aes256")) {
995+ result = Checksum.CKSUMTYPE_HMAC_SHA1_96_AES256;
996+ } else if (input.equalsIgnoreCase("hmac-md5-rc4") ||
997+ input.equalsIgnoreCase("hmac-md5-arcfour") ||
998+ input.equalsIgnoreCase("hmac-md5-enc")) {
999+ result = Checksum.CKSUMTYPE_HMAC_MD5_ARCFOUR;
1000+ } else if (input.equalsIgnoreCase("NULL")) {
1001+ result = EncryptedData.ETYPE_NULL;
1002+ }
1003+
1004+ return result;
1005+ }
1006+
1007+ /**
1008+ * Resets the default kdc realm.
1009+ * We do not need to synchronize these methods since assignments are atomic
1010+ *
1011+ * This method was useless. Kept here in case some class still calls it.
1012+ */
1013+ public void resetDefaultRealm(String realm) {
1014+ if (DEBUG) {
1015+ System.out.println(">>> Config try resetting default kdc " + realm);
1016+ }
1017+ }
1018+
1019+ /**
1020+ * Check to use addresses in tickets
1021+ * use addresses if "no_addresses" or "noaddresses" is set to false
1022+ */
1023+ public boolean useAddresses() {
1024+ boolean useAddr = false;
1025+ // use addresses if "no_addresses" is set to false
1026+ String value = getDefault("no_addresses", "libdefaults");
1027+ useAddr = (value != null && value.equalsIgnoreCase("false"));
1028+ if (useAddr == false) {
1029+ // use addresses if "noaddresses" is set to false
1030+ value = getDefault("noaddresses", "libdefaults");
1031+ useAddr = (value != null && value.equalsIgnoreCase("false"));
1032+ }
1033+ return useAddr;
1034+ }
1035+
1036+ /**
1037+ * Check if need to use DNS to locate Kerberos services
1038+ */
1039+ public boolean useDNS(String name) {
1040+ String value = getDefault(name, "libdefaults");
1041+ if (value == null) {
1042+ value = getDefault("dns_fallback", "libdefaults");
1043+ if ("false".equalsIgnoreCase(value)) {
1044+ return false;
1045+ } else {
1046+ return true;
1047+ }
1048+ } else {
1049+ return value.equalsIgnoreCase("true");
1050+ }
1051+ }
1052+
1053+ /**
1054+ * Check if need to use DNS to locate the KDC
1055+ */
1056+ public boolean useDNS_KDC() {
1057+ return useDNS("dns_lookup_kdc");
1058+ }
1059+
1060+ /*
1061+ * Check if need to use DNS to locate the Realm
1062+ */
1063+ public boolean useDNS_Realm() {
1064+ return useDNS("dns_lookup_realm");
1065+ }
1066+
1067+ /**
1068+ * Gets default realm.
1069+ * @throws KrbException where no realm can be located
1070+ * @return the default realm, always non null
1071+ */
1072+ public String getDefaultRealm() throws KrbException {
1073+ if (defaultRealm != null) {
1074+ return defaultRealm;
1075+ }
1076+ Exception cause = null;
1077+ String realm = getDefault("default_realm", "libdefaults");
1078+ if ((realm == null) && useDNS_Realm()) {
1079+ // use DNS to locate Kerberos realm
1080+ try {
1081+ realm = getRealmFromDNS();
1082+ } catch (KrbException ke) {
1083+ cause = ke;
1084+ }
1085+ }
1086+ if (realm == null) {
1087+ realm = java.security.AccessController.doPrivileged(
1088+ new java.security.PrivilegedAction<String>() {
1089+ @Override
1090+ public String run() {
1091+ String osname = System.getProperty("os.name");
1092+ if (osname.startsWith("Windows")) {
1093+ return System.getenv("USERDNSDOMAIN");
1094+ }
1095+ return null;
1096+ }
1097+ });
1098+ }
1099+ if (realm == null) {
1100+ KrbException ke = new KrbException("Cannot locate default realm");
1101+ if (cause != null) {
1102+ ke.initCause(cause);
1103+ }
1104+ throw ke;
1105+ }
1106+ return realm;
1107+ }
1108+
1109+ /**
1110+ * Returns a list of KDC's with each KDC separated by a space
1111+ *
1112+ * @param realm the realm for which the KDC list is desired
1113+ * @throws KrbException if there's no way to find KDC for the realm
1114+ * @return the list of KDCs separated by a space, always non null
1115+ */
1116+ public String getKDCList(String realm) throws KrbException {
1117+ if (realm == null) {
1118+ realm = getDefaultRealm();
1119+ }
1120+ if (realm.equalsIgnoreCase(defaultRealm)) {
1121+ return defaultKDC;
1122+ }
1123+ Exception cause = null;
1124+ String kdcs = getDefault("kdc", realm);
1125+ if ((kdcs == null) && useDNS_KDC()) {
1126+ // use DNS to locate KDC
1127+ try {
1128+ kdcs = getKDCFromDNS(realm);
1129+ } catch (KrbException ke) {
1130+ cause = ke;
1131+ }
1132+ }
1133+ if (kdcs == null) {
1134+ kdcs = java.security.AccessController.doPrivileged(
1135+ new java.security.PrivilegedAction<String>() {
1136+ @Override
1137+ public String run() {
1138+ String osname = System.getProperty("os.name");
1139+ if (osname.startsWith("Windows")) {
1140+ String logonServer = System.getenv("LOGONSERVER");
1141+ if (logonServer != null
1142+ && logonServer.startsWith("\\\\")) {
1143+ logonServer = logonServer.substring(2);
1144+ }
1145+ return logonServer;
1146+ }
1147+ return null;
1148+ }
1149+ });
1150+ }
1151+ if (kdcs == null) {
1152+ if (defaultKDC != null) {
1153+ return defaultKDC;
1154+ }
1155+ KrbException ke = new KrbException("Cannot locate KDC");
1156+ if (cause != null) {
1157+ ke.initCause(cause);
1158+ }
1159+ throw ke;
1160+ }
1161+ return kdcs;
1162+ }
1163+
1164+ /**
1165+ * Locate Kerberos realm using DNS
1166+ *
1167+ * @return the Kerberos realm
1168+ */
1169+ private String getRealmFromDNS() throws KrbException {
1170+ // use DNS to locate Kerberos realm
1171+ String realm = null;
1172+ String hostName = null;
1173+ try {
1174+ hostName = InetAddress.getLocalHost().getCanonicalHostName();
1175+ } catch (UnknownHostException e) {
1176+ KrbException ke = new KrbException(Krb5.KRB_ERR_GENERIC,
1177+ "Unable to locate Kerberos realm: " + e.getMessage());
1178+ ke.initCause(e);
1179+ throw (ke);
1180+ }
1181+ // get the domain realm mapping from the configuration
1182+ String mapRealm = PrincipalName.mapHostToRealm(hostName);
1183+ if (mapRealm == null) {
1184+ // No match. Try search and/or domain in /etc/resolv.conf
1185+ List<String> srchlist = ResolverConfiguration.open().searchlist();
1186+ for (String domain: srchlist) {
1187+ realm = checkRealm(domain);
1188+ if (realm != null) {
1189+ break;
1190+ }
1191+ }
1192+ } else {
1193+ realm = checkRealm(mapRealm);
1194+ }
1195+ if (realm == null) {
1196+ throw new KrbException(Krb5.KRB_ERR_GENERIC,
1197+ "Unable to locate Kerberos realm");
1198+ }
1199+ return realm;
1200+ }
1201+
1202+ /**
1203+ * Check if the provided realm is the correct realm
1204+ * @return the realm if correct, or null otherwise
1205+ */
1206+ private static String checkRealm(String mapRealm) {
1207+ if (DEBUG) {
1208+ System.out.println("getRealmFromDNS: trying " + mapRealm);
1209+ }
1210+ String[] records = null;
1211+ String newRealm = mapRealm;
1212+ while ((records == null) && (newRealm != null)) {
1213+ // locate DNS TXT record
1214+ records = KrbServiceLocator.getKerberosService(newRealm);
1215+ newRealm = Realm.parseRealmComponent(newRealm);
1216+ // if no DNS TXT records found, try again using sub-realm
1217+ }
1218+ if (records != null) {
1219+ for (int i = 0; i < records.length; i++) {
1220+ if (records[i].equalsIgnoreCase(mapRealm)) {
1221+ return records[i];
1222+ }
1223+ }
1224+ }
1225+ return null;
1226+ }
1227+
1228+ /**
1229+ * Locate KDC using DNS
1230+ *
1231+ * @param realm the realm for which the master KDC is desired
1232+ * @return the KDC
1233+ */
1234+ private String getKDCFromDNS(String realm) throws KrbException {
1235+ // use DNS to locate KDC
1236+ String kdcs = null;
1237+ String[] srvs = null;
1238+ // locate DNS SRV record using UDP
1239+ if (DEBUG) {
1240+ System.out.println("getKDCFromDNS using UDP");
1241+ }
1242+ srvs = KrbServiceLocator.getKerberosService(realm, "_udp");
1243+ if (srvs == null) {
1244+ // locate DNS SRV record using TCP
1245+ if (DEBUG) {
1246+ System.out.println("getKDCFromDNS using UDP");
1247+ }
1248+ srvs = KrbServiceLocator.getKerberosService(realm, "_tcp");
1249+ }
1250+ if (srvs == null) {
1251+ // no DNS SRV records
1252+ throw new KrbException(Krb5.KRB_ERR_GENERIC,
1253+ "Unable to locate KDC for realm " + realm);
1254+ }
1255+ for (int i = 0; i < srvs.length; i++) {
1256+ String value = srvs[i];
1257+ for (int j = 0; j < srvs[i].length(); j++) {
1258+ // filter the KDC name
1259+ if (value.charAt(j) == ':') {
1260+ kdcs = (value.substring(0, j)).trim();
1261+ }
1262+ }
1263+ }
1264+ return kdcs;
1265+ }
1266+
1267+ private boolean fileExists(String name) {
1268+ return java.security.AccessController.doPrivileged(
1269+ new FileExistsAction(name));
1270+ }
1271+
1272+ static class FileExistsAction
1273+ implements java.security.PrivilegedAction<Boolean> {
1274+
1275+ private String fileName;
1276+
1277+ public FileExistsAction(String fileName) {
1278+ this.fileName = fileName;
1279+ }
1280+
1281+ public Boolean run() {
1282+ return new File(fileName).exists();
1283+ }
1284+ }
1285+
1286+ @Override
1287+ public String toString() {
1288+ StringBuffer sb = new StringBuffer();
1289+ toStringIndented("", stanzaTable, sb);
1290+ return sb.toString();
1291+ }
1292+ private static void toStringIndented(String prefix, Object obj,
1293+ StringBuffer sb) {
1294+ if (obj instanceof String) {
1295+ sb.append(prefix);
1296+ sb.append(obj);
1297+ sb.append('\n');
1298+ } else if (obj instanceof Hashtable) {
1299+ Hashtable tab = (Hashtable)obj;
1300+ for (Object o: tab.keySet()) {
1301+ sb.append(prefix);
1302+ sb.append(o);
1303+ sb.append(" = {\n");
1304+ toStringIndented(prefix + " ", tab.get(o), sb);
1305+ sb.append(prefix + "}\n");
1306+ }
1307+ } else if (obj instanceof Vector) {
1308+ Vector v = (Vector)obj;
1309+ for (Object o: v.toArray()) {
1310+ toStringIndented(prefix + " ", o, sb);
1311+ }
1312+ }
1313+ }
1314+}