6844193: support max_retries in krb5.conf
Reviewed-by: valeriep
This commit is contained in:
parent
46ebf10e26
commit
20fbeb53cd
jdk
src/share/classes/sun/security/krb5
test/sun/security/krb5/auto
@ -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 class Config {
|
||||
public static synchronized void refresh() throws KrbException {
|
||||
singleton = new Config();
|
||||
KeyTab.refresh();
|
||||
KrbKdcReq.KdcAccessibility.reset();
|
||||
KrbKdcReq.initStatic();
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 @@ 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)
|
||||
*/
|
||||
public static final int DEFAULT_KDC_TIMEOUT; // milliseconds
|
||||
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)
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
203
jdk/test/sun/security/krb5/auto/MaxRetries.java
Normal file
203
jdk/test/sun/security/krb5/auto/MaxRetries.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user