--- a/jdk/src/share/classes/sun/security/krb5/Config.java Thu Apr 29 15:50:40 2010 +0800
+++ b/jdk/src/share/classes/sun/security/krb5/Config.java Thu Apr 29 15:51:10 2010 +0800
@@ -1,5 +1,5 @@
/*
- * Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * Portions Copyright 2000-2010 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -109,7 +109,7 @@
public static synchronized void refresh() throws KrbException {
singleton = new Config();
KeyTab.refresh();
- KrbKdcReq.KdcAccessibility.reset();
+ KrbKdcReq.initStatic();
}
--- a/jdk/src/share/classes/sun/security/krb5/KrbKdcReq.java Thu Apr 29 15:50:40 2010 +0800
+++ b/jdk/src/share/classes/sun/security/krb5/KrbKdcReq.java Thu Apr 29 15:51:10 2010 +0800
@@ -1,5 +1,5 @@
/*
- * Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights Reserved.
+ * Portions Copyright 2000-2010 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -51,28 +51,31 @@
public abstract class KrbKdcReq {
- // Currently there is no option to specify retries
- // in the kerberos configuration file
-
- private static final int DEFAULT_KDC_RETRY_LIMIT = Krb5.KDC_RETRY_LIMIT;
+ // The following settings can be configured in [libdefaults]
+ // section of krb5.conf, which are global for all realms. Each of
+ // them can also be defined in a realm, which overrides value here.
/**
- * Default timeout period when requesting a ticket from a KDC.
- * If not specified in the configuration file,
- * a value of 30 seconds is used.
+ * max retry time for a single KDC, default Krb5.KDC_RETRY_LIMIT (3)
+ */
+ private static int defaultKdcRetryLimit;
+ /**
+ * timeout requesting a ticket from KDC, in millisec, default 30 sec
*/
- public static final int DEFAULT_KDC_TIMEOUT; // milliseconds
+ private static int defaultKdcTimeout;
+ /**
+ * max UDP packet size, default unlimited (-1)
+ */
+ private static int defaultUdpPrefLimit;
private static final boolean DEBUG = Krb5.DEBUG;
- private static int udpPrefLimit = -1;
-
private static final String BAD_POLICY_KEY = "krb5.kdc.bad.policy";
/**
* What to do when a KDC is unavailable, specified in the
* java.security file with key krb5.kdc.bad.policy.
- * Possible values can be TRY_LAST or TRY_LESS
+ * Possible values can be TRY_LAST or TRY_LESS. Reloaded when refreshed.
*/
private enum BpType {
NONE, TRY_LAST, TRY_LESS
@@ -80,9 +83,16 @@
private static int tryLessMaxRetries = 1;
private static int tryLessTimeout = 5000;
- private static final BpType badPolicy;
+ private static BpType badPolicy;
static {
+ initStatic();
+ }
+
+ /**
+ * Read global settings
+ */
+ public static void initStatic() {
String value = AccessController.doPrivileged(
new PrivilegedAction<String>() {
public String run() {
@@ -95,9 +105,21 @@
if ("tryless".equals(ss[0])) {
if (ss.length > 1) {
String[] params = ss[1].split(",");
- tryLessMaxRetries = Integer.parseInt(params[0]);
- if (params.length > 1) {
- tryLessTimeout = Integer.parseInt(params[1]);
+ try {
+ int tmp0 = Integer.parseInt(params[0]);
+ if (params.length > 1) {
+ tryLessTimeout = Integer.parseInt(params[1]);
+ }
+ // Assign here in case of exception at params[1]
+ tryLessMaxRetries = tmp0;
+ } catch (NumberFormatException nfe) {
+ // Ignored. Please note that tryLess is recognized and
+ // used, parameters using default values
+ if (DEBUG) {
+ System.out.println("Invalid " + BAD_POLICY_KEY +
+ " parameter for tryLess: " +
+ value + ", use default");
+ }
}
}
badPolicy = BpType.TRY_LESS;
@@ -110,30 +132,33 @@
badPolicy = BpType.NONE;
}
- /*
- * Get default timeout.
- */
int timeout = -1;
+ int max_retries = -1;
+ int udf_pref_limit = -1;
+
try {
Config cfg = Config.getInstance();
String temp = cfg.getDefault("kdc_timeout", "libdefaults");
timeout = parsePositiveIntString(temp);
+ temp = cfg.getDefault("max_retries", "libdefaults");
+ max_retries = parsePositiveIntString(temp);
temp = cfg.getDefault("udp_preference_limit", "libdefaults");
- udpPrefLimit = parsePositiveIntString(temp);
+ udf_pref_limit = parsePositiveIntString(temp);
} catch (Exception exc) {
- // ignore any exceptions; use the default time out values
+ // ignore any exceptions; use default values
if (DEBUG) {
- System.out.println ("Exception in getting kdc_timeout value, " +
- "using default value " +
+ System.out.println ("Exception in getting KDC communication " +
+ "settings, using default value " +
exc.getMessage());
}
}
+ defaultKdcTimeout = timeout > 0 ? timeout : 30*1000; // 30 seconds
+ defaultKdcRetryLimit =
+ max_retries > 0 ? max_retries : Krb5.KDC_RETRY_LIMIT;
+ defaultUdpPrefLimit = udf_pref_limit;
- if (timeout > 0)
- DEFAULT_KDC_TIMEOUT = timeout;
- else
- DEFAULT_KDC_TIMEOUT = 30*1000; // 30 seconds
+ KdcAccessibility.reset();
}
protected byte[] obuf;
@@ -151,6 +176,9 @@
public String send(String realm)
throws IOException, KrbException {
+ int udpPrefLimit = getRealmSpecificValue(
+ realm, "udp_preference_limit", defaultUdpPrefLimit);
+
boolean useTCP = (udpPrefLimit > 0 &&
(obuf != null && obuf.length > udpPrefLimit));
@@ -213,9 +241,10 @@
return;
int port = Krb5.KDC_INET_DEFAULT_PORT;
- int retries = DEFAULT_KDC_RETRY_LIMIT;
- int timeout = getKdcTimeout(realm);
-
+ int retries = getRealmSpecificValue(
+ realm, "max_retries", defaultKdcRetryLimit);
+ int timeout = getRealmSpecificValue(
+ realm, "kdc_timeout", defaultKdcTimeout);
if (badPolicy == BpType.TRY_LESS &&
KdcAccessibility.isBad(tempKdc)) {
if (retries > tryLessMaxRetries) {
@@ -322,6 +351,12 @@
if (useTCP) {
TCPClient kdcClient = new TCPClient(kdc, port);
+ if (DEBUG) {
+ System.out.println(">>> KDCCommunication: kdc=" + kdc
+ + " TCP:"
+ + port
+ + ", #bytes=" + obuf.length);
+ }
try {
/*
* Send the data to the kdc.
@@ -336,7 +371,7 @@
}
} else {
- // For each KDC we try DEFAULT_KDC_RETRY_LIMIT (3) times to
+ // For each KDC we try defaultKdcRetryLimit times to
// get the response
for (int i=1; i <= retries; i++) {
UDPClient kdcClient = new UDPClient(kdc, port, timeout);
@@ -382,37 +417,37 @@
}
/**
- * Returns a timeout value for the KDC of the given realm.
- * A KDC-specific timeout, if specified in the config file,
- * overrides the default timeout (which may also be specified
- * in the config file). Default timeout is returned if null
- * is specified for realm.
- * @param realm the realm which kdc's timeout is requested
- * @return KDC timeout
+ * Returns krb5.conf setting of {@code key} for a specfic realm,
+ * which can be:
+ * 1. defined in the sub-stanza for the given realm inside [realms], or
+ * 2. defined in [libdefaults], or
+ * 3. defValue
+ * @param realm the given realm in which the setting is requested. Returns
+ * the global setting if null
+ * @param key the key for the setting
+ * @param defValue default value
+ * @return a value for the key
*/
- private int getKdcTimeout(String realm)
- {
- int timeout = DEFAULT_KDC_TIMEOUT;
+ private int getRealmSpecificValue(String realm, String key, int defValue) {
+ int v = defValue;
- if (realm == null)
- return timeout;
+ if (realm == null) return v;
- int tempTimeout = -1;
+ int temp = -1;
try {
- String temp =
- Config.getInstance().getDefault("kdc_timeout", realm);
- tempTimeout = parsePositiveIntString(temp);
+ String value =
+ Config.getInstance().getDefault(key, realm);
+ temp = parsePositiveIntString(value);
} catch (Exception exc) {
+ // Ignored, defValue will be picked up
}
- if (tempTimeout > 0)
- timeout = tempTimeout;
+ if (temp > 0) v = temp;
- return timeout;
+ return v;
}
- private static int parsePositiveIntString(String intString)
- {
+ private static int parsePositiveIntString(String intString) {
if (intString == null)
return -1;
@@ -461,7 +496,7 @@
return bads.contains(kdc);
}
- public static synchronized void reset() {
+ private static synchronized void reset() {
if (DEBUG) {
System.out.println(">>> KdcAccessibility: reset");
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/MaxRetries.java Thu Apr 29 15:51:10 2010 +0800
@@ -0,0 +1,203 @@
+/*
+ * Copyright 2010 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 6844193
+ * @run main/timeout=300 MaxRetries
+ * @summary support max_retries in krb5.conf
+ */
+
+import java.io.*;
+import java.security.Security;
+
+public class MaxRetries {
+ public static void main(String[] args)
+ throws Exception {
+
+ System.setProperty("sun.security.krb5.debug", "true");
+ new OneKDC(null).writeJAASConf();
+ System.setProperty("java.security.krb5.conf", "alternative-krb5.conf");
+
+ // For tryLast
+ Security.setProperty("krb5.kdc.bad.policy", "trylast");
+ rewriteMaxRetries(4);
+ test1(4000, 6); // 1 1 1 1 2 2
+ test1(4000, 2); // 2 2
+
+ rewriteMaxRetries(1);
+ test1(1000, 3); // 1 2 2
+ test1(1000, 2); // 2 2
+
+ rewriteMaxRetries(-1);
+ test1(5000, 4); // 1 1 2 2
+ test1(5000, 2); // 2 2
+
+ // For tryLess
+ Security.setProperty("krb5.kdc.bad.policy", "tryless");
+ rewriteMaxRetries(4);
+ test1(4000, 7); // 1 1 1 1 2 1 2
+ test1(4000, 4); // 1 2 1 2
+
+ rewriteMaxRetries(1);
+ test1(1000, 4); // 1 2 1 2
+ test1(1000, 4); // 1 2 1 2
+
+ rewriteMaxRetries(-1);
+ test1(5000, 5); // 1 1 2 1 2
+ test1(5000, 4); // 1 2 1 2
+
+ rewriteUdpPrefLimit(-1, -1); // default, no limit
+ test2("UDP");
+
+ rewriteUdpPrefLimit(10, -1); // global rules
+ test2("TCP");
+
+ rewriteUdpPrefLimit(10, 10000); // realm rules
+ test2("UDP");
+
+ rewriteUdpPrefLimit(10000, 10); // realm rules
+ test2("TCP");
+ }
+
+ /**
+ * One round of test for max_retries and timeout.
+ * @param timeout the expected timeout
+ * @param count the expected total try
+ */
+ private static void test1(int timeout, int count) throws Exception {
+ String timeoutTag = "timeout=" + timeout;
+ ByteArrayOutputStream bo = new ByteArrayOutputStream();
+ PrintStream oldout = System.out;
+ System.setOut(new PrintStream(bo));
+ Context c = Context.fromJAAS("client");
+ System.setOut(oldout);
+
+ String[] lines = new String(bo.toByteArray()).split("\n");
+ System.out.println("----------------- TEST (" + timeout + "," +
+ count + ") -----------------");
+ for (String line: lines) {
+ if (line.startsWith(">>> KDCCommunication")) {
+ System.out.println(line);
+ if (line.indexOf(timeoutTag) < 0) {
+ throw new Exception("Wrong timeout value");
+ }
+ count--;
+ }
+ }
+ if (count != 0) {
+ throw new Exception("Retry count is " + count + " less");
+ }
+ }
+
+ /**
+ * One round of test for udp_preference_limit.
+ * @param proto the expected protocol used
+ */
+ private static void test2(String proto) throws Exception {
+ ByteArrayOutputStream bo = new ByteArrayOutputStream();
+ PrintStream oldout = System.out;
+ System.setOut(new PrintStream(bo));
+ Context c = Context.fromJAAS("client");
+ System.setOut(oldout);
+
+ int count = 2;
+ String[] lines = new String(bo.toByteArray()).split("\n");
+ System.out.println("----------------- TEST -----------------");
+ for (String line: lines) {
+ if (line.startsWith(">>> KDCCommunication")) {
+ System.out.println(line);
+ count--;
+ if (line.indexOf(proto) < 0) {
+ throw new Exception("Wrong timeout value");
+ }
+ }
+ }
+ if (count != 0) {
+ throw new Exception("Retry count is " + count + " less");
+ }
+ }
+
+ /**
+ * Set udp_preference_limit for global and realm
+ */
+ private static void rewriteUdpPrefLimit(int global, int realm)
+ throws Exception {
+ BufferedReader fr = new BufferedReader(new FileReader(OneKDC.KRB5_CONF));
+ FileWriter fw = new FileWriter("alternative-krb5.conf");
+ while (true) {
+ String s = fr.readLine();
+ if (s == null) {
+ break;
+ }
+ if (s.startsWith("[realms]")) {
+ // Reconfig global setting
+ if (global != -1) {
+ fw.write("udp_preference_limit = " + global + "\n");
+ }
+ } else if (s.trim().startsWith("kdc = ")) {
+ if (realm != -1) {
+ // Reconfig for realm
+ fw.write(" udp_preference_limit = " + realm + "\n");
+ }
+ }
+ fw.write(s + "\n");
+ }
+ fr.close();
+ fw.close();
+ sun.security.krb5.Config.refresh();
+ }
+
+ /**
+ * Set max_retries and timeout value for realm. The global value is always
+ * 2 and 5000.
+ * @param value max_retries and timeout/1000 for a realm, -1 means none.
+ */
+ private static void rewriteMaxRetries(int value) throws Exception {
+ BufferedReader fr = new BufferedReader(new FileReader(OneKDC.KRB5_CONF));
+ FileWriter fw = new FileWriter("alternative-krb5.conf");
+ while (true) {
+ String s = fr.readLine();
+ if (s == null) {
+ break;
+ }
+ if (s.startsWith("[realms]")) {
+ // Reconfig global setting
+ fw.write("max_retries = 2\n");
+ fw.write("kdc_timeout = 5000\n");
+ } else if (s.trim().startsWith("kdc = ")) {
+ if (value != -1) {
+ // Reconfig for realm
+ fw.write(" max_retries = " + value + "\n");
+ fw.write(" kdc_timeout = " + (value*1000) + "\n");
+ }
+ // Add a bad KDC as the first candidate
+ fw.write(" kdc = localhost:33333\n");
+ }
+ fw.write(s + "\n");
+ }
+ fr.close();
+ fw.close();
+ sun.security.krb5.Config.refresh();
+ }
+}