001: /*
002: * Portions Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: /*
027: * @(#)KrbKdcReq.java 1.24 07/05/05
028: *
029: * (C) Copyright IBM Corp. 1999 All Rights Reserved.
030: * Copyright 1997 The Open Group Research Institute. All rights reserved.
031: */
032:
033: package sun.security.krb5;
034:
035: import sun.security.krb5.internal.Krb5;
036: import sun.security.krb5.internal.UDPClient;
037: import sun.security.krb5.internal.TCPClient;
038: import java.io.IOException;
039: import java.io.InterruptedIOException;
040: import java.net.SocketTimeoutException;
041: import java.net.UnknownHostException;
042: import java.util.StringTokenizer;
043: import java.security.AccessController;
044: import java.security.PrivilegedExceptionAction;
045: import java.security.PrivilegedActionException;
046:
047: public abstract class KrbKdcReq {
048:
049: /**
050: * Default port for a KDC.
051: */
052: private static final int DEFAULT_KDC_PORT = Krb5.KDC_INET_DEFAULT_PORT;
053:
054: // Currently there is no option to specify retries
055: // in the kerberos configuration file
056:
057: private static final int DEFAULT_KDC_RETRY_LIMIT = Krb5.KDC_RETRY_LIMIT;
058:
059: /**
060: * Default timeout period when requesting a ticket from a KDC.
061: * If not specified in the configuration file,
062: * a value of 30 seconds is used.
063: */
064: public static final int DEFAULT_KDC_TIMEOUT; // milliseconds
065:
066: private static final boolean DEBUG = Krb5.DEBUG;
067:
068: private static int udpPrefLimit = -1;
069:
070: static {
071:
072: /*
073: * Get default timeout.
074: */
075:
076: int timeout = -1;
077: try {
078: Config cfg = Config.getInstance();
079: String temp = cfg.getDefault("kdc_timeout", "libdefaults");
080: timeout = parsePositiveIntString(temp);
081: temp = cfg
082: .getDefault("udp_preference_limit", "libdefaults");
083: udpPrefLimit = parsePositiveIntString(temp);
084: } catch (Exception exc) {
085: // ignore any exceptions; use the default time out values
086: if (DEBUG) {
087: System.out
088: .println("Exception in getting kdc_timeout value, "
089: + "using default value "
090: + exc.getMessage());
091: }
092: }
093:
094: if (timeout > 0)
095: DEFAULT_KDC_TIMEOUT = timeout;
096: else
097: DEFAULT_KDC_TIMEOUT = 30 * 1000; // 30 seconds
098: }
099:
100: protected byte[] obuf;
101: protected byte[] ibuf;
102:
103: /**
104: * Sends the provided data to the KDC of the specified realm.
105: * Returns the response from the KDC.
106: * Default realm/KDC is used if realm is null.
107: * @param realm the realm of the KDC where data is to be sent.
108: * @returns the kdc to which the AS request was sent to
109: * @exception InterruptedIOException if timeout expires
110: * @exception KrbException
111: */
112:
113: public String send(String realm) throws IOException, KrbException {
114: boolean useTCP = (udpPrefLimit > 0 && (obuf != null && obuf.length > udpPrefLimit));
115:
116: return (send(realm, useTCP));
117: }
118:
119: public String send(String realm, boolean useTCP)
120: throws IOException, KrbException {
121:
122: if (obuf == null)
123: return null;
124: Exception savedException = null;
125: Config cfg = Config.getInstance();
126:
127: if (realm == null) {
128: realm = cfg.getDefaultRealm();
129: if (realm == null) {
130: throw new KrbException(Krb5.KRB_ERR_GENERIC,
131: "Cannot find default realm");
132: }
133: }
134:
135: /*
136: * Get timeout.
137: */
138:
139: int timeout = getKdcTimeout(realm);
140:
141: String kdcList = cfg.getKDCList(realm);
142: if (kdcList == null) {
143: throw new KrbException("Cannot get kdc for realm " + realm);
144: }
145: String tempKdc = null; // may include the port number also
146: StringTokenizer st = new StringTokenizer(kdcList);
147: while (st.hasMoreTokens()) {
148: tempKdc = st.nextToken();
149: try {
150: send(realm, tempKdc, useTCP);
151: break;
152: } catch (Exception e) {
153: savedException = e;
154: }
155: }
156: if (ibuf == null && savedException != null) {
157: if (savedException instanceof IOException) {
158: throw (IOException) savedException;
159: } else {
160: throw (KrbException) savedException;
161: }
162: }
163: return tempKdc;
164: }
165:
166: // send the AS Request to the specified KDC
167:
168: public void send(String realm, String tempKdc, boolean useTCP)
169: throws IOException, KrbException {
170:
171: if (obuf == null)
172: return;
173: PrivilegedActionException savedException = null;
174: int port = Krb5.KDC_INET_DEFAULT_PORT;
175:
176: /*
177: * Get timeout.
178: */
179: int timeout = getKdcTimeout(realm);
180: /*
181: * Get port number for this KDC.
182: */
183: StringTokenizer strTok = new StringTokenizer(tempKdc, ":");
184: String kdc = strTok.nextToken();
185: if (strTok.hasMoreTokens()) {
186: String portStr = strTok.nextToken();
187: int tempPort = parsePositiveIntString(portStr);
188: if (tempPort > 0)
189: port = tempPort;
190: }
191:
192: if (DEBUG) {
193: System.out.println(">>> KrbKdcReq send: kdc=" + kdc
194: + (useTCP ? " TCP:" : " UDP:") + port
195: + ", timeout=" + timeout + ", number of retries ="
196: + DEFAULT_KDC_RETRY_LIMIT + ", #bytes="
197: + obuf.length);
198: }
199:
200: KdcCommunication kdcCommunication = new KdcCommunication(kdc,
201: port, useTCP, timeout, obuf);
202: try {
203: ibuf = AccessController.doPrivileged(kdcCommunication);
204: if (DEBUG) {
205: System.out.println(">>> KrbKdcReq send: #bytes read="
206: + (ibuf != null ? ibuf.length : 0));
207: }
208: } catch (PrivilegedActionException e) {
209: Exception wrappedException = e.getException();
210: if (wrappedException instanceof IOException) {
211: throw (IOException) wrappedException;
212: } else {
213: throw (KrbException) wrappedException;
214: }
215: }
216: if (DEBUG) {
217: System.out.println(">>> KrbKdcReq send: #bytes read="
218: + (ibuf != null ? ibuf.length : 0));
219: }
220: }
221:
222: private static class KdcCommunication implements
223: PrivilegedExceptionAction<byte[]> {
224:
225: private String kdc;
226: private int port;
227: private boolean useTCP;
228: private int timeout;
229: private byte[] obuf;
230:
231: public KdcCommunication(String kdc, int port, boolean useTCP,
232: int timeout, byte[] obuf) {
233: this .kdc = kdc;
234: this .port = port;
235: this .useTCP = useTCP;
236: this .timeout = timeout;
237: this .obuf = obuf;
238: }
239:
240: // The caller only casts IOException and KrbException so don't
241: // add any new ones!
242:
243: public byte[] run() throws IOException, KrbException {
244:
245: byte[] ibuf = null;
246:
247: if (useTCP) {
248: TCPClient kdcClient = new TCPClient(kdc, port);
249: try {
250: /*
251: * Send the data to the kdc.
252: */
253: kdcClient.send(obuf);
254: /*
255: * And get a response.
256: */
257: ibuf = kdcClient.receive();
258: } finally {
259: kdcClient.close();
260: }
261:
262: } else {
263: // For each KDC we try DEFAULT_KDC_RETRY_LIMIT (3) times to
264: // get the response
265: for (int i = 1; i <= DEFAULT_KDC_RETRY_LIMIT; i++) {
266: UDPClient kdcClient = new UDPClient(kdc, port,
267: timeout);
268:
269: if (DEBUG) {
270: System.out.println(">>> KDCCommunication: kdc="
271: + kdc + (useTCP ? " TCP:" : " UDP:")
272: + port + ", timeout=" + timeout
273: + ",Attempt =" + i + ", #bytes="
274: + obuf.length);
275: }
276: /*
277: * Send the data to the kdc.
278: */
279:
280: kdcClient.send(obuf);
281:
282: /*
283: * And get a response.
284: */
285: try {
286: ibuf = kdcClient.receive();
287: break;
288: } catch (SocketTimeoutException se) {
289: if (DEBUG) {
290: System.out
291: .println("SocketTimeOutException with "
292: + "attempt: " + i);
293: }
294: if (i == DEFAULT_KDC_RETRY_LIMIT) {
295: ibuf = null;
296: throw se;
297: }
298: }
299: }
300: }
301: return ibuf;
302: }
303: }
304:
305: /**
306: * Returns a timeout value for the KDC of the given realm.
307: * A KDC-specific timeout, if specified in the config file,
308: * overrides the default timeout (which may also be specified
309: * in the config file). Default timeout is returned if null
310: * is specified for realm.
311: * @param realm the realm which kdc's timeout is requested
312: * @return KDC timeout
313: */
314: private int getKdcTimeout(String realm) {
315: int timeout = DEFAULT_KDC_TIMEOUT;
316:
317: if (realm == null)
318: return timeout;
319:
320: int tempTimeout = -1;
321: try {
322: String temp = Config.getInstance().getDefault(
323: "kdc_timeout", realm);
324: tempTimeout = parsePositiveIntString(temp);
325: } catch (Exception exc) {
326: }
327:
328: if (tempTimeout > 0)
329: timeout = tempTimeout;
330:
331: return timeout;
332: }
333:
334: private static int parsePositiveIntString(String intString) {
335: if (intString == null)
336: return -1;
337:
338: int ret = -1;
339:
340: try {
341: ret = Integer.parseInt(intString);
342: } catch (Exception exc) {
343: return -1;
344: }
345:
346: if (ret >= 0)
347: return ret;
348:
349: return -1;
350: }
351: }
|