blob: e9ff9cc04d5fe9f7e3aba44bc337fb71b84d4e51 [file] [log] [blame]
/*
* Portions Copyright 2000-2006 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. Sun designates this
* particular file as subject to the "Classpath" exception as provided
* by Sun in the LICENSE file that accompanied this code.
*
* 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.
*/
/*
*
* (C) Copyright IBM Corp. 1999 All Rights Reserved.
* Copyright 1997 The Open Group Research Institute. All rights reserved.
*/
package sun.security.krb5;
import sun.security.krb5.internal.Krb5;
import sun.security.krb5.internal.UDPClient;
import sun.security.krb5.internal.TCPClient;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.StringTokenizer;
import java.security.AccessController;
import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
public abstract class KrbKdcReq {
/**
* Default port for a KDC.
*/
private static final int DEFAULT_KDC_PORT = Krb5.KDC_INET_DEFAULT_PORT;
// 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;
/**
* Default timeout period when requesting a ticket from a KDC.
* If not specified in the configuration file,
* a value of 30 seconds is used.
*/
public static final int DEFAULT_KDC_TIMEOUT; // milliseconds
private static final boolean DEBUG = Krb5.DEBUG;
private static int udpPrefLimit = -1;
static {
/*
* Get default timeout.
*/
int timeout = -1;
try {
Config cfg = Config.getInstance();
String temp = cfg.getDefault("kdc_timeout", "libdefaults");
timeout = parsePositiveIntString(temp);
temp = cfg.getDefault("udp_preference_limit", "libdefaults");
udpPrefLimit = parsePositiveIntString(temp);
} catch (Exception exc) {
// ignore any exceptions; use the default time out values
if (DEBUG) {
System.out.println ("Exception in getting kdc_timeout value, " +
"using default value " +
exc.getMessage());
}
}
if (timeout > 0)
DEFAULT_KDC_TIMEOUT = timeout;
else
DEFAULT_KDC_TIMEOUT = 30*1000; // 30 seconds
}
protected byte[] obuf;
protected byte[] ibuf;
/**
* Sends the provided data to the KDC of the specified realm.
* Returns the response from the KDC.
* Default realm/KDC is used if realm is null.
* @param realm the realm of the KDC where data is to be sent.
* @returns the kdc to which the AS request was sent to
* @exception InterruptedIOException if timeout expires
* @exception KrbException
*/
public String send(String realm)
throws IOException, KrbException {
boolean useTCP = (udpPrefLimit > 0 &&
(obuf != null && obuf.length > udpPrefLimit));
return (send(realm, useTCP));
}
public String send(String realm, boolean useTCP)
throws IOException, KrbException {
if (obuf == null)
return null;
Exception savedException = null;
Config cfg = Config.getInstance();
if (realm == null) {
realm = cfg.getDefaultRealm();
if (realm == null) {
throw new KrbException(Krb5.KRB_ERR_GENERIC,
"Cannot find default realm");
}
}
/*
* Get timeout.
*/
int timeout = getKdcTimeout(realm);
String kdcList = cfg.getKDCList(realm);
if (kdcList == null) {
throw new KrbException("Cannot get kdc for realm " + realm);
}
String tempKdc = null; // may include the port number also
StringTokenizer st = new StringTokenizer(kdcList);
while (st.hasMoreTokens()) {
tempKdc = st.nextToken();
try {
send(realm,tempKdc,useTCP);
break;
} catch (Exception e) {
savedException = e;
}
}
if (ibuf == null && savedException != null) {
if (savedException instanceof IOException) {
throw (IOException) savedException;
} else {
throw (KrbException) savedException;
}
}
return tempKdc;
}
// send the AS Request to the specified KDC
public void send(String realm, String tempKdc, boolean useTCP)
throws IOException, KrbException {
if (obuf == null)
return;
PrivilegedActionException savedException = null;
int port = Krb5.KDC_INET_DEFAULT_PORT;
/*
* Get timeout.
*/
int timeout = getKdcTimeout(realm);
/*
* Get port number for this KDC.
*/
StringTokenizer strTok = new StringTokenizer(tempKdc, ":");
String kdc = strTok.nextToken();
if (strTok.hasMoreTokens()) {
String portStr = strTok.nextToken();
int tempPort = parsePositiveIntString(portStr);
if (tempPort > 0)
port = tempPort;
}
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: kdc=" + kdc
+ (useTCP ? " TCP:":" UDP:")
+ port + ", timeout="
+ timeout
+ ", number of retries ="
+ DEFAULT_KDC_RETRY_LIMIT
+ ", #bytes=" + obuf.length);
}
KdcCommunication kdcCommunication =
new KdcCommunication(kdc, port, useTCP, timeout, obuf);
try {
ibuf = AccessController.doPrivileged(kdcCommunication);
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: #bytes read="
+ (ibuf != null ? ibuf.length : 0));
}
} catch (PrivilegedActionException e) {
Exception wrappedException = e.getException();
if (wrappedException instanceof IOException) {
throw (IOException) wrappedException;
} else {
throw (KrbException) wrappedException;
}
}
if (DEBUG) {
System.out.println(">>> KrbKdcReq send: #bytes read="
+ (ibuf != null ? ibuf.length : 0));
}
}
private static class KdcCommunication
implements PrivilegedExceptionAction<byte[]> {
private String kdc;
private int port;
private boolean useTCP;
private int timeout;
private byte[] obuf;
public KdcCommunication(String kdc, int port, boolean useTCP,
int timeout, byte[] obuf) {
this.kdc = kdc;
this.port = port;
this.useTCP = useTCP;
this.timeout = timeout;
this.obuf = obuf;
}
// The caller only casts IOException and KrbException so don't
// add any new ones!
public byte[] run() throws IOException, KrbException {
byte[] ibuf = null;
if (useTCP) {
TCPClient kdcClient = new TCPClient(kdc, port);
try {
/*
* Send the data to the kdc.
*/
kdcClient.send(obuf);
/*
* And get a response.
*/
ibuf = kdcClient.receive();
} finally {
kdcClient.close();
}
} else {
// For each KDC we try DEFAULT_KDC_RETRY_LIMIT (3) times to
// get the response
for (int i=1; i <= DEFAULT_KDC_RETRY_LIMIT; i++) {
UDPClient kdcClient = new UDPClient(kdc, port, timeout);
if (DEBUG) {
System.out.println(">>> KDCCommunication: kdc=" + kdc
+ (useTCP ? " TCP:":" UDP:")
+ port + ", timeout="
+ timeout
+ ",Attempt =" + i
+ ", #bytes=" + obuf.length);
}
/*
* Send the data to the kdc.
*/
kdcClient.send(obuf);
/*
* And get a response.
*/
try {
ibuf = kdcClient.receive();
break;
} catch (SocketTimeoutException se) {
if (DEBUG) {
System.out.println ("SocketTimeOutException with " +
"attempt: " + i);
}
if (i == DEFAULT_KDC_RETRY_LIMIT) {
ibuf = null;
throw se;
}
}
}
}
return ibuf;
}
}
/**
* 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
*/
private int getKdcTimeout(String realm)
{
int timeout = DEFAULT_KDC_TIMEOUT;
if (realm == null)
return timeout;
int tempTimeout = -1;
try {
String temp =
Config.getInstance().getDefault("kdc_timeout", realm);
tempTimeout = parsePositiveIntString(temp);
} catch (Exception exc) {
}
if (tempTimeout > 0)
timeout = tempTimeout;
return timeout;
}
private static int parsePositiveIntString(String intString)
{
if (intString == null)
return -1;
int ret = -1;
try {
ret = Integer.parseInt(intString);
} catch (Exception exc) {
return -1;
}
if (ret >= 0)
return ret;
return -1;
}
}