History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/krb5/KdcComm.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/krb5/KdcComm.java+516−0
@@ -0,0 +1,516 @@
1/*
2 * Copyright (c) 2000, 2011, Oracle and/or its affiliates. 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. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * 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 java.security.PrivilegedAction;
35import java.security.Security;
36import java.util.Locale;
37import sun.security.krb5.internal.Krb5;
38import sun.security.krb5.internal.NetClient;
39import java.io.IOException;
40import java.net.SocketTimeoutException;
41import java.util.StringTokenizer;
42import java.security.AccessController;
43import java.security.PrivilegedExceptionAction;
44import java.security.PrivilegedActionException;
45import java.util.ArrayList;
46import java.util.List;
47import java.util.Set;
48import java.util.HashSet;
49import sun.security.krb5.internal.KRBError;
50
51/**
52 * KDC-REQ/KDC-REP communication. No more base class for KrbAsReq and
53 * KrbTgsReq. This class is now communication only.
54 */
55public final class KdcComm {
56
57 // The following settings can be configured in [libdefaults]
58 // section of krb5.conf, which are global for all realms. Each of
59 // them can also be defined in a realm, which overrides value here.
60
61 /**
62 * max retry time for a single KDC, default Krb5.KDC_RETRY_LIMIT (3)
63 */
64 private static int defaultKdcRetryLimit;
65 /**
66 * timeout requesting a ticket from KDC, in millisec, default 30 sec
67 */
68 private static int defaultKdcTimeout;
69 /**
70 * max UDP packet size, default unlimited (-1)
71 */
72 private static int defaultUdpPrefLimit;
73
74 private static final boolean DEBUG = Krb5.DEBUG;
75
76 private static final String BAD_POLICY_KEY = "krb5.kdc.bad.policy";
77
78 /**
79 * What to do when a KDC is unavailable, specified in the
80 * java.security file with key krb5.kdc.bad.policy.
81 * Possible values can be TRY_LAST or TRY_LESS. Reloaded when refreshed.
82 */
83 private enum BpType {
84 NONE, TRY_LAST, TRY_LESS
85 }
86 private static int tryLessMaxRetries = 1;
87 private static int tryLessTimeout = 5000;
88
89 private static BpType badPolicy;
90
91 static {
92 initStatic();
93 }
94
95 /**
96 * Read global settings
97 */
98 public static void initStatic() {
99 String value = AccessController.doPrivileged(
100 new PrivilegedAction<String>() {
101 public String run() {
102 return Security.getProperty(BAD_POLICY_KEY);
103 }
104 });
105 if (value != null) {
106 value = value.toLowerCase(Locale.ENGLISH);
107 String[] ss = value.split(":");
108 if ("tryless".equals(ss[0])) {
109 if (ss.length > 1) {
110 String[] params = ss[1].split(",");
111 try {
112 int tmp0 = Integer.parseInt(params[0]);
113 if (params.length > 1) {
114 tryLessTimeout = Integer.parseInt(params[1]);
115 }
116 // Assign here in case of exception at params[1]
117 tryLessMaxRetries = tmp0;
118 } catch (NumberFormatException nfe) {
119 // Ignored. Please note that tryLess is recognized and
120 // used, parameters using default values
121 if (DEBUG) {
122 System.out.println("Invalid " + BAD_POLICY_KEY +
123 " parameter for tryLess: " +
124 value + ", use default");
125 }
126 }
127 }
128 badPolicy = BpType.TRY_LESS;
129 } else if ("trylast".equals(ss[0])) {
130 badPolicy = BpType.TRY_LAST;
131 } else {
132 badPolicy = BpType.NONE;
133 }
134 } else {
135 badPolicy = BpType.NONE;
136 }
137
138
139 int timeout = -1;
140 int max_retries = -1;
141 int udf_pref_limit = -1;
142
143 try {
144 Config cfg = Config.getInstance();
145 String temp = cfg.getDefault("kdc_timeout", "libdefaults");
146 timeout = parsePositiveIntString(temp);
147 temp = cfg.getDefault("max_retries", "libdefaults");
148 max_retries = parsePositiveIntString(temp);
149 temp = cfg.getDefault("udp_preference_limit", "libdefaults");
150 udf_pref_limit = parsePositiveIntString(temp);
151 } catch (Exception exc) {
152 // ignore any exceptions; use default values
153 if (DEBUG) {
154 System.out.println ("Exception in getting KDC communication " +
155 "settings, using default value " +
156 exc.getMessage());
157 }
158 }
159 defaultKdcTimeout = timeout > 0 ? timeout : 30*1000; // 30 seconds
160 defaultKdcRetryLimit =
161 max_retries > 0 ? max_retries : Krb5.KDC_RETRY_LIMIT;
162 defaultUdpPrefLimit = udf_pref_limit;
163
164 KdcAccessibility.reset();
165 }
166
167 /**
168 * The instance fields
169 */
170 private String realm;
171
172 public KdcComm(String realm) throws KrbException {
173 if (realm == null) {
174 realm = Config.getInstance().getDefaultRealm();
175 if (realm == null) {
176 throw new KrbException(Krb5.KRB_ERR_GENERIC,
177 "Cannot find default realm");
178 }
179 }
180 this.realm = realm;
181 }
182
183 public byte[] send(byte[] obuf)
184 throws IOException, KrbException {
185 int udpPrefLimit = getRealmSpecificValue(
186 realm, "udp_preference_limit", defaultUdpPrefLimit);
187
188 boolean useTCP = (udpPrefLimit > 0 &&
189 (obuf != null && obuf.length > udpPrefLimit));
190
191 return send(obuf, useTCP);
192 }
193
194 private byte[] send(byte[] obuf, boolean useTCP)
195 throws IOException, KrbException {
196
197 if (obuf == null)
198 return null;
199 Exception savedException = null;
200 Config cfg = Config.getInstance();
201
202 if (realm == null) {
203 realm = cfg.getDefaultRealm();
204 if (realm == null) {
205 throw new KrbException(Krb5.KRB_ERR_GENERIC,
206 "Cannot find default realm");
207 }
208 }
209
210 String kdcList = cfg.getKDCList(realm);
211 if (kdcList == null) {
212 throw new KrbException("Cannot get kdc for realm " + realm);
213 }
214 String tempKdc = null; // may include the port number also
215 byte[] ibuf = null;
216 for (String tmp: KdcAccessibility.list(kdcList)) {
217 tempKdc = tmp;
218 try {
219 ibuf = send(obuf,tempKdc,useTCP);
220 KRBError ke = null;
221 try {
222 ke = new KRBError(ibuf);
223 } catch (Exception e) {
224 // OK
225 }
226 if (ke != null && ke.getErrorCode() ==
227 Krb5.KRB_ERR_RESPONSE_TOO_BIG) {
228 ibuf = send(obuf, tempKdc, true);
229 }
230 KdcAccessibility.removeBad(tempKdc);
231 break;
232 } catch (Exception e) {
233 if (DEBUG) {
234 System.out.println(">>> KrbKdcReq send: error trying " +
235 tempKdc);
236 e.printStackTrace(System.out);
237 }
238 KdcAccessibility.addBad(tempKdc);
239 savedException = e;
240 }
241 }
242 if (ibuf == null && savedException != null) {
243 if (savedException instanceof IOException) {
244 throw (IOException) savedException;
245 } else {
246 throw (KrbException) savedException;
247 }
248 }
249 return ibuf;
250 }
251
252 // send the AS Request to the specified KDC
253
254 private byte[] send(byte[] obuf, String tempKdc, boolean useTCP)
255 throws IOException, KrbException {
256
257 if (obuf == null)
258 return null;
259
260 int port = Krb5.KDC_INET_DEFAULT_PORT;
261 int retries = getRealmSpecificValue(
262 realm, "max_retries", defaultKdcRetryLimit);
263 int timeout = getRealmSpecificValue(
264 realm, "kdc_timeout", defaultKdcTimeout);
265 if (badPolicy == BpType.TRY_LESS &&
266 KdcAccessibility.isBad(tempKdc)) {
267 if (retries > tryLessMaxRetries) {
268 retries = tryLessMaxRetries; // less retries
269 }
270 if (timeout > tryLessTimeout) {
271 timeout = tryLessTimeout; // less time
272 }
273 }
274
275 String kdc = null;
276 String portStr = null;
277
278 if (tempKdc.charAt(0) == '[') { // Explicit IPv6 in []
279 int pos = tempKdc.indexOf(']', 1);
280 if (pos == -1) {
281 throw new IOException("Illegal KDC: " + tempKdc);
282 }
283 kdc = tempKdc.substring(1, pos);
284 if (pos != tempKdc.length() - 1) { // with port number
285 if (tempKdc.charAt(pos+1) != ':') {
286 throw new IOException("Illegal KDC: " + tempKdc);
287 }
288 portStr = tempKdc.substring(pos+2);
289 }
290 } else {
291 int colon = tempKdc.indexOf(':');
292 if (colon == -1) { // Hostname or IPv4 host only
293 kdc = tempKdc;
294 } else {
295 int nextColon = tempKdc.indexOf(':', colon+1);
296 if (nextColon > 0) { // >=2 ":", IPv6 with no port
297 kdc = tempKdc;
298 } else { // 1 ":", hostname or IPv4 with port
299 kdc = tempKdc.substring(0, colon);
300 portStr = tempKdc.substring(colon+1);
301 }
302 }
303 }
304 if (portStr != null) {
305 int tempPort = parsePositiveIntString(portStr);
306 if (tempPort > 0)
307 port = tempPort;
308 }
309
310 if (DEBUG) {
311 System.out.println(">>> KrbKdcReq send: kdc=" + kdc
312 + (useTCP ? " TCP:":" UDP:")
313 + port + ", timeout="
314 + timeout
315 + ", number of retries ="
316 + retries
317 + ", #bytes=" + obuf.length);
318 }
319
320 KdcCommunication kdcCommunication =
321 new KdcCommunication(kdc, port, useTCP, timeout, retries, obuf);
322 try {
323 byte[] ibuf = AccessController.doPrivileged(kdcCommunication);
324 if (DEBUG) {
325 System.out.println(">>> KrbKdcReq send: #bytes read="
326 + (ibuf != null ? ibuf.length : 0));
327 }
328 return ibuf;
329 } catch (PrivilegedActionException e) {
330 Exception wrappedException = e.getException();
331 if (wrappedException instanceof IOException) {
332 throw (IOException) wrappedException;
333 } else {
334 throw (KrbException) wrappedException;
335 }
336 }
337 }
338
339 private static class KdcCommunication
340 implements PrivilegedExceptionAction<byte[]> {
341
342 private String kdc;
343 private int port;
344 private boolean useTCP;
345 private int timeout;
346 private int retries;
347 private byte[] obuf;
348
349 public KdcCommunication(String kdc, int port, boolean useTCP,
350 int timeout, int retries, byte[] obuf) {
351 this.kdc = kdc;
352 this.port = port;
353 this.useTCP = useTCP;
354 this.timeout = timeout;
355 this.retries = retries;
356 this.obuf = obuf;
357 }
358
359 // The caller only casts IOException and KrbException so don't
360 // add any new ones!
361
362 public byte[] run() throws IOException, KrbException {
363
364 byte[] ibuf = null;
365
366 for (int i=1; i <= retries; i++) {
367 String proto = useTCP?"TCP":"UDP";
368 NetClient kdcClient = NetClient.getInstance(
369 proto, kdc, port, timeout);
370 if (DEBUG) {
371 System.out.println(">>> KDCCommunication: kdc=" + kdc
372 + " " + proto + ":"
373 + port + ", timeout="
374 + timeout
375 + ",Attempt =" + i
376 + ", #bytes=" + obuf.length);
377 }
378 try {
379 /*
380 * Send the data to the kdc.
381 */
382 kdcClient.send(obuf);
383 /*
384 * And get a response.
385 */
386 ibuf = kdcClient.receive();
387 break;
388 } catch (SocketTimeoutException se) {
389 if (DEBUG) {
390 System.out.println ("SocketTimeOutException with " +
391 "attempt: " + i);
392 }
393 if (i == retries) {
394 ibuf = null;
395 throw se;
396 }
397 } finally {
398 kdcClient.close();
399 }
400 }
401 return ibuf;
402 }
403 }
404
405 /**
406 * Returns krb5.conf setting of {@code key} for a specfic realm,
407 * which can be:
408 * 1. defined in the sub-stanza for the given realm inside [realms], or
409 * 2. defined in [libdefaults], or
410 * 3. defValue
411 * @param realm the given realm in which the setting is requested. Returns
412 * the global setting if null
413 * @param key the key for the setting
414 * @param defValue default value
415 * @return a value for the key
416 */
417 private int getRealmSpecificValue(String realm, String key, int defValue) {
418 int v = defValue;
419
420 if (realm == null) return v;
421
422 int temp = -1;
423 try {
424 String value =
425 Config.getInstance().getDefault(key, realm);
426 temp = parsePositiveIntString(value);
427 } catch (Exception exc) {
428 // Ignored, defValue will be picked up
429 }
430
431 if (temp > 0) v = temp;
432
433 return v;
434 }
435
436 private static int parsePositiveIntString(String intString) {
437 if (intString == null)
438 return -1;
439
440 int ret = -1;
441
442 try {
443 ret = Integer.parseInt(intString);
444 } catch (Exception exc) {
445 return -1;
446 }
447
448 if (ret >= 0)
449 return ret;
450
451 return -1;
452 }
453
454 /**
455 * Maintains a KDC accessible list. Unavailable KDCs are put into a
456 * blacklist, when a KDC in the blacklist is available, it's removed
457 * from there. No insertion order in the blacklist.
458 *
459 * There are two methods to deal with KDCs in the blacklist. 1. Only try
460 * them when there's no KDC not on the blacklist. 2. Still try them, but
461 * with lesser number of retries and smaller timeout value.
462 */
463 static class KdcAccessibility {
464 // Known bad KDCs
465 private static Set<String> bads = new HashSet<>();
466
467 private static synchronized void addBad(String kdc) {
468 if (DEBUG) {
469 System.out.println(">>> KdcAccessibility: add " + kdc);
470 }
471 bads.add(kdc);
472 }
473
474 private static synchronized void removeBad(String kdc) {
475 if (DEBUG) {
476 System.out.println(">>> KdcAccessibility: remove " + kdc);
477 }
478 bads.remove(kdc);
479 }
480
481 private static synchronized boolean isBad(String kdc) {
482 return bads.contains(kdc);
483 }
484
485 private static synchronized void reset() {
486 if (DEBUG) {
487 System.out.println(">>> KdcAccessibility: reset");
488 }
489 bads.clear();
490 }
491
492 // Returns a preferred KDC list by putting the bad ones at the end
493 private static synchronized String[] list(String kdcList) {
494 StringTokenizer st = new StringTokenizer(kdcList);
495 List<String> list = new ArrayList<>();
496 if (badPolicy == BpType.TRY_LAST) {
497 List<String> badkdcs = new ArrayList<>();
498 while (st.hasMoreTokens()) {
499 String t = st.nextToken();
500 if (bads.contains(t)) badkdcs.add(t);
501 else list.add(t);
502 }
503 // Bad KDCs are put at last
504 list.addAll(badkdcs);
505 } else {
506 // All KDCs are returned in their original order,
507 // This include TRY_LESS and NONE
508 while (st.hasMoreTokens()) {
509 list.add(st.nextToken());
510 }
511 }
512 return list.toArray(new String[list.size()]);
513 }
514 }
515}
516