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