Skip to content
This repository was archived by the owner on Sep 29, 2021. It is now read-only.

Commit 37ad8d8

Browse files
author
mbalao
committedJun 5, 2019
8215032: Support Kerberos cross-realm referrals (RFC 6806)
Reviewed-by: weijun
1 parent 48155d1 commit 37ad8d8

25 files changed

+933
-204
lines changed
 

‎src/java.base/share/conf/security/java.security

+25
Original file line numberDiff line numberDiff line change
@@ -474,6 +474,31 @@ networkaddress.cache.negative.ttl=10
474474
#
475475
krb5.kdc.bad.policy = tryLast
476476

477+
#
478+
# Kerberos cross-realm referrals (RFC 6806)
479+
#
480+
# OpenJDK's Kerberos client supports cross-realm referrals as defined in
481+
# RFC 6806. This allows to setup more dynamic environments in which clients
482+
# do not need to know in advance how to reach the realm of a target principal
483+
# (either a user or service).
484+
#
485+
# When a client issues an AS or a TGS request, the "canonicalize" option
486+
# is set to announce support of this feature. A KDC server may fulfill the
487+
# request or reply referring the client to a different one. If referred,
488+
# the client will issue a new request and the cycle repeats.
489+
#
490+
# In addition to referrals, the "canonicalize" option allows the KDC server
491+
# to change the client name in response to an AS request. For security reasons,
492+
# RFC 6806 (section 11) FAST scheme is enforced.
493+
#
494+
# Disable Kerberos cross-realm referrals. Value may be overwritten with a
495+
# System property (-Dsun.security.krb5.disableReferrals).
496+
sun.security.krb5.disableReferrals=false
497+
498+
# Maximum number of AS or TGS referrals to avoid infinite loops. Value may
499+
# be overwritten with a System property (-Dsun.security.krb5.maxReferrals).
500+
sun.security.krb5.maxReferrals=5
501+
477502
#
478503
# Algorithm restrictions for certification path (CertPath) processing
479504
#

‎src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosPrincipal.java

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -80,6 +80,11 @@ public final class KerberosPrincipal
8080

8181
public static final int KRB_NT_UID = 5;
8282

83+
/**
84+
* Enterprise name (alias)
85+
*/
86+
public static final int KRB_NT_ENTERPRISE = 10;
87+
8388
private transient String fullName;
8489

8590
private transient String realm;

‎src/java.security.jgss/share/classes/sun/security/krb5/Checksum.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -218,7 +218,7 @@ boolean isEqual(Checksum cksum) throws KdcErrException {
218218
* @exception IOException if an I/O error occurs while reading encoded data.
219219
*
220220
*/
221-
private Checksum(DerValue encoding) throws Asn1Exception, IOException {
221+
public Checksum(DerValue encoding) throws Asn1Exception, IOException {
222222
DerValue der;
223223
if (encoding.getTag() != DerValue.tag_Sequence) {
224224
throw new Asn1Exception(Krb5.ASN1_BAD_ID);

‎src/java.security.jgss/share/classes/sun/security/krb5/Config.java

+37-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -47,6 +47,7 @@
4747
import sun.net.dns.ResolverConfiguration;
4848
import sun.security.krb5.internal.crypto.EType;
4949
import sun.security.krb5.internal.Krb5;
50+
import sun.security.util.SecurityProperties;
5051

5152
/**
5253
* This class maintains key-value pairs of Kerberos configurable constants
@@ -55,6 +56,41 @@
5556

5657
public class Config {
5758

59+
/**
60+
* {@systemProperty sun.security.krb5.disableReferrals} property
61+
* indicating whether or not cross-realm referrals (RFC 6806) are
62+
* enabled.
63+
*/
64+
public static final boolean DISABLE_REFERRALS;
65+
66+
/**
67+
* {@systemProperty sun.security.krb5.maxReferrals} property
68+
* indicating the maximum number of cross-realm referral
69+
* hops allowed.
70+
*/
71+
public static final int MAX_REFERRALS;
72+
73+
static {
74+
String disableReferralsProp =
75+
SecurityProperties.privilegedGetOverridable(
76+
"sun.security.krb5.disableReferrals");
77+
if (disableReferralsProp != null) {
78+
DISABLE_REFERRALS = "true".equalsIgnoreCase(disableReferralsProp);
79+
} else {
80+
DISABLE_REFERRALS = false;
81+
}
82+
83+
int maxReferralsValue = 5;
84+
String maxReferralsProp =
85+
SecurityProperties.privilegedGetOverridable(
86+
"sun.security.krb5.maxReferrals");
87+
try {
88+
maxReferralsValue = Integer.parseInt(maxReferralsProp);
89+
} catch (NumberFormatException e) {
90+
}
91+
MAX_REFERRALS = maxReferralsValue;
92+
}
93+
5894
/*
5995
* Only allow a single instance of Config.
6096
*/

‎src/java.security.jgss/share/classes/sun/security/krb5/KrbAsRep.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -155,11 +155,11 @@ private void decrypt(EncryptionKey dkey, KrbAsReq asReq)
155155
rep.encKDCRepPart = enc_part;
156156

157157
ASReq req = asReq.getMessage();
158-
check(true, req, rep);
158+
check(true, req, rep, dkey);
159159

160160
creds = new Credentials(
161161
rep.ticket,
162-
req.reqBody.cname,
162+
rep.cname,
163163
enc_part.sname,
164164
enc_part.key,
165165
enc_part.flags,

‎src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReq.java

+13-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -36,6 +36,7 @@
3636
import sun.security.krb5.internal.crypto.KeyUsage;
3737
import java.io.IOException;
3838
import java.time.Instant;
39+
import java.util.Arrays;
3940

4041
/**
4142
* This class encapsulates the KRB-AS-REQ message that the client
@@ -58,7 +59,8 @@ public KrbAsReq(EncryptionKey pakey, // ok
5859
KerberosTime till, // ok, will use
5960
KerberosTime rtime, // ok
6061
int[] eTypes, // NO
61-
HostAddresses addresses // ok
62+
HostAddresses addresses, // ok
63+
PAData[] extraPAs // ok
6264
)
6365
throws KrbException, IOException {
6466

@@ -93,6 +95,15 @@ public KrbAsReq(EncryptionKey pakey, // ok
9395
paData[0] = new PAData( Krb5.PA_ENC_TIMESTAMP,
9496
encTs.asn1Encode());
9597
}
98+
if (extraPAs != null && extraPAs.length > 0) {
99+
if (paData == null) {
100+
paData = new PAData[extraPAs.length];
101+
} else {
102+
paData = Arrays.copyOf(paData, paData.length + extraPAs.length);
103+
}
104+
System.arraycopy(extraPAs, 0, paData,
105+
paData.length - extraPAs.length, extraPAs.length);
106+
}
96107

97108
if (cname.getRealm() == null) {
98109
throw new RealmException(Krb5.REALM_NULL,

‎src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java

+77-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2012, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 2019, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -262,7 +262,9 @@ public void setAddresses(HostAddresses addresses) {
262262
* @throws KrbException
263263
* @throws IOException
264264
*/
265-
private KrbAsReq build(EncryptionKey key) throws KrbException, IOException {
265+
private KrbAsReq build(EncryptionKey key, ReferralsState referralsState)
266+
throws KrbException, IOException {
267+
PAData[] extraPAs = null;
266268
int[] eTypes;
267269
if (password != null) {
268270
eTypes = EType.getDefaults("default_tkt_enctypes");
@@ -272,6 +274,14 @@ private KrbAsReq build(EncryptionKey key) throws KrbException, IOException {
272274
ks);
273275
for (EncryptionKey k: ks) k.destroy();
274276
}
277+
options = (options == null) ? new KDCOptions() : options;
278+
if (referralsState.isEnabled()) {
279+
options.set(KDCOptions.CANONICALIZE, true);
280+
extraPAs = new PAData[]{ new PAData(Krb5.PA_REQ_ENC_PA_REP,
281+
new byte[]{}) };
282+
} else {
283+
options.set(KDCOptions.CANONICALIZE, false);
284+
}
275285
return new KrbAsReq(key,
276286
options,
277287
cname,
@@ -280,7 +290,8 @@ private KrbAsReq build(EncryptionKey key) throws KrbException, IOException {
280290
till,
281291
rtime,
282292
eTypes,
283-
addresses);
293+
addresses,
294+
extraPAs);
284295
}
285296

286297
/**
@@ -318,11 +329,15 @@ private KrbAsReqBuilder resolve()
318329
*/
319330
private KrbAsReqBuilder send() throws KrbException, IOException {
320331
boolean preAuthFailedOnce = false;
321-
KdcComm comm = new KdcComm(cname.getRealmAsString());
332+
KdcComm comm = null;
322333
EncryptionKey pakey = null;
334+
ReferralsState referralsState = new ReferralsState();
323335
while (true) {
336+
if (referralsState.refreshComm()) {
337+
comm = new KdcComm(cname.getRealmAsString());
338+
}
324339
try {
325-
req = build(pakey);
340+
req = build(pakey, referralsState);
326341
rep = new KrbAsRep(comm.send(req.encoding()));
327342
return this;
328343
} catch (KrbException ke) {
@@ -351,12 +366,69 @@ private KrbAsReqBuilder send() throws KrbException, IOException {
351366
}
352367
paList = kerr.getPA(); // Update current paList
353368
} else {
369+
if (referralsState.handleError(ke)) {
370+
continue;
371+
}
354372
throw ke;
355373
}
356374
}
357375
}
358376
}
359377

378+
private final class ReferralsState {
379+
private boolean enabled;
380+
private int count;
381+
private boolean refreshComm;
382+
383+
ReferralsState() throws KrbException {
384+
if (Config.DISABLE_REFERRALS) {
385+
if (cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
386+
throw new KrbException("NT-ENTERPRISE principals only allowed" +
387+
" when referrals are enabled.");
388+
}
389+
enabled = false;
390+
} else {
391+
enabled = true;
392+
}
393+
refreshComm = true;
394+
}
395+
396+
boolean handleError(KrbException ke) throws RealmException {
397+
if (enabled) {
398+
if (ke.returnCode() == Krb5.KRB_ERR_WRONG_REALM) {
399+
Realm referredRealm = ke.getError().getClientRealm();
400+
if (req.getMessage().reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) &&
401+
referredRealm != null && referredRealm.toString().length() > 0 &&
402+
count < Config.MAX_REFERRALS) {
403+
cname = new PrincipalName(cname.getNameType(),
404+
cname.getNameStrings(), referredRealm);
405+
refreshComm = true;
406+
count++;
407+
return true;
408+
}
409+
}
410+
if (count < Config.MAX_REFERRALS &&
411+
cname.getNameType() != PrincipalName.KRB_NT_ENTERPRISE) {
412+
// Server may raise an error if CANONICALIZE is true.
413+
// Try CANONICALIZE false.
414+
enabled = false;
415+
return true;
416+
}
417+
}
418+
return false;
419+
}
420+
421+
boolean refreshComm() {
422+
boolean retRefreshComm = refreshComm;
423+
refreshComm = false;
424+
return retRefreshComm;
425+
}
426+
427+
boolean isEnabled() {
428+
return enabled;
429+
}
430+
}
431+
360432
/**
361433
* Performs AS-REQ send and AS-REP receive.
362434
* Maybe a state is needed here, to divide prepare process and getCreds.

0 commit comments

Comments
 (0)