blob: e9ff9cc04d5fe9f7e3aba44bc337fb71b84d4e51 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Portions Copyright 2000-2006 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26/*
27 *
28 * (C) Copyright IBM Corp. 1999 All Rights Reserved.
29 * Copyright 1997 The Open Group Research Institute. All rights reserved.
30 */
31
32package sun.security.krb5;
33
34import sun.security.krb5.internal.Krb5;
35import sun.security.krb5.internal.UDPClient;
36import sun.security.krb5.internal.TCPClient;
37import java.io.IOException;
38import java.io.InterruptedIOException;
39import java.net.SocketTimeoutException;
40import java.net.UnknownHostException;
41import java.util.StringTokenizer;
42import java.security.AccessController;
43import java.security.PrivilegedExceptionAction;
44import java.security.PrivilegedActionException;
45
46public abstract class KrbKdcReq {
47
48 /**
49 * Default port for a KDC.
50 */
51 private static final int DEFAULT_KDC_PORT = Krb5.KDC_INET_DEFAULT_PORT;
52
53 // Currently there is no option to specify retries
54 // in the kerberos configuration file
55
56 private static final int DEFAULT_KDC_RETRY_LIMIT = Krb5.KDC_RETRY_LIMIT;
57
58 /**
59 * Default timeout period when requesting a ticket from a KDC.
60 * If not specified in the configuration file,
61 * a value of 30 seconds is used.
62 */
63 public static final int DEFAULT_KDC_TIMEOUT; // milliseconds
64
65 private static final boolean DEBUG = Krb5.DEBUG;
66
67 private static int udpPrefLimit = -1;
68
69 static {
70
71 /*
72 * Get default timeout.
73 */
74
75 int timeout = -1;
76 try {
77 Config cfg = Config.getInstance();
78 String temp = cfg.getDefault("kdc_timeout", "libdefaults");
79 timeout = parsePositiveIntString(temp);
80 temp = cfg.getDefault("udp_preference_limit", "libdefaults");
81 udpPrefLimit = parsePositiveIntString(temp);
82 } catch (Exception exc) {
83 // ignore any exceptions; use the default time out values
84 if (DEBUG) {
85 System.out.println ("Exception in getting kdc_timeout value, " +
86 "using default value " +
87 exc.getMessage());
88 }
89 }
90
91 if (timeout > 0)
92 DEFAULT_KDC_TIMEOUT = timeout;
93 else
94 DEFAULT_KDC_TIMEOUT = 30*1000; // 30 seconds
95 }
96
97 protected byte[] obuf;
98 protected byte[] ibuf;
99
100 /**
101 * Sends the provided data to the KDC of the specified realm.
102 * Returns the response from the KDC.
103 * Default realm/KDC is used if realm is null.
104 * @param realm the realm of the KDC where data is to be sent.
105 * @returns the kdc to which the AS request was sent to
106 * @exception InterruptedIOException if timeout expires
107 * @exception KrbException
108 */
109
110 public String send(String realm)
111 throws IOException, KrbException {
112 boolean useTCP = (udpPrefLimit > 0 &&
113 (obuf != null && obuf.length > udpPrefLimit));
114
115 return (send(realm, useTCP));
116 }
117
118 public String send(String realm, boolean useTCP)
119 throws IOException, KrbException {
120
121 if (obuf == null)
122 return null;
123 Exception savedException = null;
124 Config cfg = Config.getInstance();
125
126 if (realm == null) {
127 realm = cfg.getDefaultRealm();
128 if (realm == null) {
129 throw new KrbException(Krb5.KRB_ERR_GENERIC,
130 "Cannot find default realm");
131 }
132 }
133
134 /*
135 * Get timeout.
136 */
137
138 int timeout = getKdcTimeout(realm);
139
140 String kdcList = cfg.getKDCList(realm);
141 if (kdcList == null) {
142 throw new KrbException("Cannot get kdc for realm " + realm);
143 }
144 String tempKdc = null; // may include the port number also
145 StringTokenizer st = new StringTokenizer(kdcList);
146 while (st.hasMoreTokens()) {
147 tempKdc = st.nextToken();
148 try {
149 send(realm,tempKdc,useTCP);
150 break;
151 } catch (Exception e) {
152 savedException = e;
153 }
154 }
155 if (ibuf == null && savedException != null) {
156 if (savedException instanceof IOException) {
157 throw (IOException) savedException;
158 } else {
159 throw (KrbException) savedException;
160 }
161 }
162 return tempKdc;
163 }
164
165 // send the AS Request to the specified KDC
166
167 public void send(String realm, String tempKdc, boolean useTCP)
168 throws IOException, KrbException {
169
170 if (obuf == null)
171 return;
172 PrivilegedActionException savedException = null;
173 int port = Krb5.KDC_INET_DEFAULT_PORT;
174
175 /*
176 * Get timeout.
177 */
178 int timeout = getKdcTimeout(realm);
179 /*
180 * Get port number for this KDC.
181 */
182 StringTokenizer strTok = new StringTokenizer(tempKdc, ":");
183 String kdc = strTok.nextToken();
184 if (strTok.hasMoreTokens()) {
185 String portStr = strTok.nextToken();
186 int tempPort = parsePositiveIntString(portStr);
187 if (tempPort > 0)
188 port = tempPort;
189 }
190
191 if (DEBUG) {
192 System.out.println(">>> KrbKdcReq send: kdc=" + kdc
193 + (useTCP ? " TCP:":" UDP:")
194 + port + ", timeout="
195 + timeout
196 + ", number of retries ="
197 + DEFAULT_KDC_RETRY_LIMIT
198 + ", #bytes=" + obuf.length);
199 }
200
201 KdcCommunication kdcCommunication =
202 new KdcCommunication(kdc, port, useTCP, timeout, obuf);
203 try {
204 ibuf = AccessController.doPrivileged(kdcCommunication);
205 if (DEBUG) {
206 System.out.println(">>> KrbKdcReq send: #bytes read="
207 + (ibuf != null ? ibuf.length : 0));
208 }
209 } catch (PrivilegedActionException e) {
210 Exception wrappedException = e.getException();
211 if (wrappedException instanceof IOException) {
212 throw (IOException) wrappedException;
213 } else {
214 throw (KrbException) wrappedException;
215 }
216 }
217 if (DEBUG) {
218 System.out.println(">>> KrbKdcReq send: #bytes read="
219 + (ibuf != null ? ibuf.length : 0));
220 }
221 }
222
223 private static class KdcCommunication
224 implements PrivilegedExceptionAction<byte[]> {
225
226 private String kdc;
227 private int port;
228 private boolean useTCP;
229 private int timeout;
230 private byte[] obuf;
231
232 public KdcCommunication(String kdc, int port, boolean useTCP,
233 int timeout, byte[] obuf) {
234 this.kdc = kdc;
235 this.port = port;
236 this.useTCP = useTCP;
237 this.timeout = timeout;
238 this.obuf = obuf;
239 }
240
241 // The caller only casts IOException and KrbException so don't
242 // add any new ones!
243
244 public byte[] run() throws IOException, KrbException {
245
246 byte[] ibuf = null;
247
248 if (useTCP) {
249 TCPClient kdcClient = new TCPClient(kdc, port);
250 try {
251 /*
252 * Send the data to the kdc.
253 */
254 kdcClient.send(obuf);
255 /*
256 * And get a response.
257 */
258 ibuf = kdcClient.receive();
259 } finally {
260 kdcClient.close();
261 }
262
263 } else {
264 // For each KDC we try DEFAULT_KDC_RETRY_LIMIT (3) times to
265 // get the response
266 for (int i=1; i <= DEFAULT_KDC_RETRY_LIMIT; i++) {
267 UDPClient kdcClient = new UDPClient(kdc, port, timeout);
268
269 if (DEBUG) {
270 System.out.println(">>> KDCCommunication: kdc=" + kdc
271 + (useTCP ? " TCP:":" UDP:")
272 + port + ", timeout="
273 + timeout
274 + ",Attempt =" + i
275 + ", #bytes=" + obuf.length);
276 }
277 /*
278 * Send the data to the kdc.
279 */
280
281 kdcClient.send(obuf);
282
283 /*
284 * And get a response.
285 */
286 try {
287 ibuf = kdcClient.receive();
288 break;
289 } catch (SocketTimeoutException se) {
290 if (DEBUG) {
291 System.out.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 {
316 int timeout = DEFAULT_KDC_TIMEOUT;
317
318 if (realm == null)
319 return timeout;
320
321 int tempTimeout = -1;
322 try {
323 String temp =
324 Config.getInstance().getDefault("kdc_timeout", realm);
325 tempTimeout = parsePositiveIntString(temp);
326 } catch (Exception exc) {
327 }
328
329 if (tempTimeout > 0)
330 timeout = tempTimeout;
331
332 return timeout;
333 }
334
335 private static int parsePositiveIntString(String intString)
336 {
337 if (intString == null)
338 return -1;
339
340 int ret = -1;
341
342 try {
343 ret = Integer.parseInt(intString);
344 } catch (Exception exc) {
345 return -1;
346 }
347
348 if (ret >= 0)
349 return ret;
350
351 return -1;
352 }
353}