PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/share/classes/sun/security/krb5/Config.java

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