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