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+
32+package sun.security.krb5;
33+
34+import java.security.PrivilegedAction;
35+import java.security.Security;
36+import java.util.Locale;
37+import sun.security.krb5.internal.Krb5;
38+import sun.security.krb5.internal.NetClient;
39+import java.io.IOException;
40+import java.net.SocketTimeoutException;
41+import java.util.StringTokenizer;
42+import java.security.AccessController;
43+import java.security.PrivilegedExceptionAction;
44+import java.security.PrivilegedActionException;
45+import java.util.ArrayList;
46+import java.util.List;
47+import java.util.Set;
48+import java.util.HashSet;
49+import 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+ */
55+public 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+