jdk/src/share/classes/sun/security/krb5/KrbKdcReq.java
changeset 2 90ce3da70b43
child 2597 31ee39bed77f
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/krb5/KrbKdcReq.java	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,353 @@
+/*
+ * 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;
+    }
+}