提交 5024f9d3 编写于 作者: W weijun

6844193: support max_retries in krb5.conf

Reviewed-by: valeriep
上级 d3f7a280
/*
* 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 class Config {
public static synchronized void refresh() throws KrbException {
singleton = new Config();
KeyTab.refresh();
KrbKdcReq.KdcAccessibility.reset();
KrbKdcReq.initStatic();
}
......
/*
* 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 @@ import java.util.HashSet;
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
*/
private static int defaultKdcTimeout;
/**
* max UDP packet size, default unlimited (-1)
*/
public static final int DEFAULT_KDC_TIMEOUT; // milliseconds
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 @@ public abstract class KrbKdcReq {
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 @@ public abstract class KrbKdcReq {
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 @@ public abstract class KrbKdcReq {
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 abstract class KrbKdcReq {
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 @@ public abstract class KrbKdcReq {
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 @@ public abstract class KrbKdcReq {
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 @@ public abstract class KrbKdcReq {
}
} 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 @@ public abstract class KrbKdcReq {
}
/**
* 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 @@ public abstract class KrbKdcReq {
return bads.contains(kdc);
}
public static synchronized void reset() {
private static synchronized void reset() {
if (DEBUG) {
System.out.println(">>> KdcAccessibility: reset");
}
......
/*
* 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();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册