/* * Copyright (c) 2000, 2006, Oracle and/or its affiliates. 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. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.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); } try { /* * 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; } } } finally { kdcClient.close(); } } } 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; } }