PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

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

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