Skip to content

Commit bdfc60a

Browse files
committed
8270137: Kerberos Credential Retrieval from Cache not Working in Cross-Realm Setup
Backport-of: 67869b491ae1eaf311dfb8c61a9e94329a822ffc
1 parent 5f9429b commit bdfc60a

File tree

3 files changed

+88
-50
lines changed

3 files changed

+88
-50
lines changed

src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2001, 2021, 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
@@ -53,22 +53,22 @@ private static enum S4U2Type {
5353

5454
/**
5555
* Used by a middle server to acquire credentials on behalf of a
56-
* client to itself using the S4U2self extension.
57-
* @param client the client to impersonate
56+
* user to itself using the S4U2self extension.
57+
* @param user the user to impersonate
5858
* @param ccreds the TGT of the middle service
59-
* @return the new creds (cname=client, sname=middle)
59+
* @return the new creds (cname=user, sname=middle)
6060
*/
61-
public static Credentials acquireS4U2selfCreds(PrincipalName client,
61+
public static Credentials acquireS4U2selfCreds(PrincipalName user,
6262
Credentials ccreds) throws KrbException, IOException {
6363
if (!ccreds.isForwardable()) {
6464
throw new KrbException("S4U2self needs a FORWARDABLE ticket");
6565
}
6666
PrincipalName sname = ccreds.getClient();
67-
String uRealm = client.getRealmString();
67+
String uRealm = user.getRealmString();
6868
String localRealm = ccreds.getClient().getRealmString();
6969
if (!uRealm.equals(localRealm)) {
7070
// Referrals will be required because the middle service
71-
// and the client impersonated are on different realms.
71+
// and the user impersonated are on different realms.
7272
if (Config.DISABLE_REFERRALS) {
7373
throw new KrbException("Cross-realm S4U2Self request not" +
7474
" possible when referrals are disabled.");
@@ -87,18 +87,18 @@ public static Credentials acquireS4U2selfCreds(PrincipalName client,
8787
}
8888
Credentials creds = serviceCreds(
8989
KDCOptions.with(KDCOptions.FORWARDABLE),
90-
ccreds, ccreds.getClient(), sname, null,
91-
new PAData[] {
90+
ccreds, ccreds.getClient(), sname, user,
91+
null, new PAData[] {
9292
new PAData(Krb5.PA_FOR_USER,
93-
new PAForUserEnc(client,
93+
new PAForUserEnc(user,
9494
ccreds.getSessionKey()).asn1Encode()),
9595
new PAData(Krb5.PA_PAC_OPTIONS,
9696
new PaPacOptions()
9797
.setResourceBasedConstrainedDelegation(true)
9898
.setClaims(true)
9999
.asn1Encode())
100100
}, S4U2Type.SELF);
101-
if (!creds.getClient().equals(client)) {
101+
if (!creds.getClient().equals(user)) {
102102
throw new KrbException("S4U2self request not honored by KDC");
103103
}
104104
if (!creds.isForwardable()) {
@@ -136,7 +136,7 @@ public static Credentials acquireS4U2proxyCreds(
136136
}
137137
Credentials creds = serviceCreds(KDCOptions.with(
138138
KDCOptions.CNAME_IN_ADDL_TKT, KDCOptions.FORWARDABLE),
139-
ccreds, ccreds.getClient(), backendPrincipal,
139+
ccreds, ccreds.getClient(), backendPrincipal, null,
140140
new Ticket[] {second}, new PAData[] {
141141
new PAData(Krb5.PA_PAC_OPTIONS,
142142
new PaPacOptions()
@@ -313,7 +313,7 @@ private static Credentials serviceCreds(
313313
throws KrbException, IOException {
314314
return serviceCreds(new KDCOptions(), ccreds,
315315
ccreds.getClient(), service, null, null,
316-
S4U2Type.NONE);
316+
null, S4U2Type.NONE);
317317
}
318318

319319
/*
@@ -325,21 +325,21 @@ private static Credentials serviceCreds(
325325
private static Credentials serviceCreds(
326326
KDCOptions options, Credentials asCreds,
327327
PrincipalName cname, PrincipalName sname,
328-
Ticket[] additionalTickets, PAData[] extraPAs,
329-
S4U2Type s4u2Type)
328+
PrincipalName user, Ticket[] additionalTickets,
329+
PAData[] extraPAs, S4U2Type s4u2Type)
330330
throws KrbException, IOException {
331331
if (!Config.DISABLE_REFERRALS) {
332332
try {
333333
return serviceCredsReferrals(options, asCreds, cname, sname,
334-
s4u2Type, additionalTickets, extraPAs);
334+
s4u2Type, user, additionalTickets, extraPAs);
335335
} catch (KrbException e) {
336336
// Server may raise an error if CANONICALIZE is true.
337337
// Try CANONICALIZE false.
338338
}
339339
}
340340
return serviceCredsSingle(options, asCreds, cname,
341341
asCreds.getClientAlias(), sname, sname, s4u2Type,
342-
additionalTickets, extraPAs);
342+
user, additionalTickets, extraPAs);
343343
}
344344

345345
/*
@@ -349,8 +349,8 @@ private static Credentials serviceCreds(
349349
private static Credentials serviceCredsReferrals(
350350
KDCOptions options, Credentials asCreds,
351351
PrincipalName cname, PrincipalName sname,
352-
S4U2Type s4u2Type, Ticket[] additionalTickets,
353-
PAData[] extraPAs)
352+
S4U2Type s4u2Type, PrincipalName user,
353+
Ticket[] additionalTickets, PAData[] extraPAs)
354354
throws KrbException, IOException {
355355
options = new KDCOptions(options.toBooleanArray());
356356
options.set(KDCOptions.CANONICALIZE, true);
@@ -362,12 +362,13 @@ private static Credentials serviceCredsReferrals(
362362
PrincipalName clientAlias = asCreds.getClientAlias();
363363
while (referrals.size() <= Config.MAX_REFERRALS) {
364364
ReferralsCache.ReferralCacheEntry ref =
365-
ReferralsCache.get(cname, sname, refSname.getRealmString());
365+
ReferralsCache.get(cname, sname, user,
366+
additionalTickets, refSname.getRealmString());
366367
String toRealm = null;
367368
if (ref == null) {
368369
creds = serviceCredsSingle(options, asCreds, cname,
369370
clientAlias, refSname, cSname, s4u2Type,
370-
additionalTickets, extraPAs);
371+
user, additionalTickets, extraPAs);
371372
PrincipalName server = creds.getServer();
372373
if (!refSname.equals(server)) {
373374
String[] serverNameStrings = server.getNameStrings();
@@ -378,15 +379,9 @@ private static Credentials serviceCredsReferrals(
378379
serverNameStrings[1])) {
379380
// Server Name (sname) has the following format:
380381
// krbtgt/TO-REALM.COM@FROM-REALM.COM
381-
if (s4u2Type == S4U2Type.NONE) {
382-
// Do not store S4U2Self or S4U2Proxy referral
383-
// TGTs in the cache. Caching such tickets is not
384-
// defined in MS-SFU and may cause unexpected
385-
// results when using them in a different context.
386-
ReferralsCache.put(cname, sname,
387-
server.getRealmString(),
388-
serverNameStrings[1], creds);
389-
}
382+
ReferralsCache.put(cname, sname, user,
383+
additionalTickets, server.getRealmString(),
384+
serverNameStrings[1], creds);
390385
toRealm = serverNameStrings[1];
391386
isReferral = true;
392387
}
@@ -411,7 +406,7 @@ private static Credentials serviceCredsReferrals(
411406
}
412407
additionalTickets[0] = credsInOut[1].getTicket();
413408
} else if (s4u2Type == S4U2Type.SELF) {
414-
handleS4U2SelfReferral(extraPAs, asCreds, creds);
409+
handleS4U2SelfReferral(extraPAs, user, creds);
415410
}
416411
if (referrals.contains(toRealm)) {
417412
// Referrals loop detected
@@ -440,8 +435,8 @@ private static Credentials serviceCredsSingle(
440435
KDCOptions options, Credentials asCreds,
441436
PrincipalName cname, PrincipalName clientAlias,
442437
PrincipalName refSname, PrincipalName sname,
443-
S4U2Type s4u2Type, Ticket[] additionalTickets,
444-
PAData[] extraPAs)
438+
S4U2Type s4u2Type, PrincipalName user,
439+
Ticket[] additionalTickets, PAData[] extraPAs)
445440
throws KrbException, IOException {
446441
Credentials theCreds = null;
447442
boolean[] okAsDelegate = new boolean[]{true};
@@ -469,7 +464,7 @@ private static Credentials serviceCredsSingle(
469464
Credentials.printDebug(newTgt);
470465
}
471466
if (s4u2Type == S4U2Type.SELF) {
472-
handleS4U2SelfReferral(extraPAs, asCreds, newTgt);
467+
handleS4U2SelfReferral(extraPAs, user, newTgt);
473468
}
474469
asCreds = newTgt;
475470
cname = asCreds.getClient();
@@ -498,19 +493,16 @@ private static Credentials serviceCredsSingle(
498493
* different realm or when using a referral TGT.
499494
*/
500495
private static void handleS4U2SelfReferral(PAData[] pas,
501-
Credentials oldCeds, Credentials newCreds)
496+
PrincipalName user, Credentials newCreds)
502497
throws Asn1Exception, KrbException, IOException {
503498
if (DEBUG) {
504499
System.out.println(">>> Handling S4U2Self referral");
505500
}
506501
for (int i = 0; i < pas.length; i++) {
507502
PAData pa = pas[i];
508503
if (pa.getType() == Krb5.PA_FOR_USER) {
509-
PAForUserEnc paForUser = new PAForUserEnc(
510-
new DerValue(pa.getValue()),
511-
oldCeds.getSessionKey());
512504
pas[i] = new PAData(Krb5.PA_FOR_USER,
513-
new PAForUserEnc(paForUser.getName(),
505+
new PAForUserEnc(user,
514506
newCreds.getSessionKey()).asn1Encode());
515507
break;
516508
}

src/java.security.jgss/share/classes/sun/security/krb5/internal/ReferralsCache.java

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, Red Hat, Inc.
2+
* Copyright (c) 2019, 2021, Red Hat, Inc.
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
@@ -25,12 +25,14 @@
2525

2626
package sun.security.krb5.internal;
2727

28+
import java.util.Arrays;
2829
import java.util.Date;
2930
import java.util.HashMap;
3031
import java.util.LinkedList;
3132
import java.util.List;
3233
import java.util.Map;
3334
import java.util.Map.Entry;
35+
import java.util.Objects;
3436

3537
import sun.security.krb5.Credentials;
3638
import sun.security.krb5.PrincipalName;
@@ -51,19 +53,33 @@ final class ReferralsCache {
5153
static private final class ReferralCacheKey {
5254
private PrincipalName cname;
5355
private PrincipalName sname;
54-
ReferralCacheKey (PrincipalName cname, PrincipalName sname) {
56+
private PrincipalName user; // S4U2Self only
57+
private byte[] userSvcTicketEnc; // S4U2Proxy only
58+
ReferralCacheKey (PrincipalName cname, PrincipalName sname,
59+
PrincipalName user, Ticket userSvcTicket) {
5560
this.cname = cname;
5661
this.sname = sname;
62+
this.user = user;
63+
if (userSvcTicket != null && userSvcTicket.encPart != null) {
64+
byte[] userSvcTicketEnc = userSvcTicket.encPart.getBytes();
65+
if (userSvcTicketEnc.length > 0) {
66+
this.userSvcTicketEnc = userSvcTicketEnc;
67+
}
68+
}
5769
}
5870
public boolean equals(Object other) {
5971
if (!(other instanceof ReferralCacheKey))
6072
return false;
6173
ReferralCacheKey that = (ReferralCacheKey)other;
6274
return cname.equals(that.cname) &&
63-
sname.equals(that.sname);
75+
sname.equals(that.sname) &&
76+
Objects.equals(user, that.user) &&
77+
Arrays.equals(userSvcTicketEnc, that.userSvcTicketEnc);
6478
}
6579
public int hashCode() {
66-
return cname.hashCode() + sname.hashCode();
80+
return cname.hashCode() + sname.hashCode() +
81+
Objects.hashCode(user) +
82+
Arrays.hashCode(userSvcTicketEnc);
6783
}
6884
}
6985

@@ -84,7 +100,8 @@ String getToRealm() {
84100

85101
/*
86102
* Add a new referral entry to the cache, including: client principal,
87-
* service principal, source KDC realm, destination KDC realm and
103+
* service principal, user principal (S4U2Self only), client service
104+
* ticket (S4U2Proxy only), source KDC realm, destination KDC realm and
88105
* referral TGT.
89106
*
90107
* If a loop is generated when adding the new referral, the first hop is
@@ -94,8 +111,12 @@ String getToRealm() {
94111
* REALM-1.COM -> REALM-2.COM referral entry is removed from the cache.
95112
*/
96113
static synchronized void put(PrincipalName cname, PrincipalName service,
97-
String fromRealm, String toRealm, Credentials creds) {
98-
ReferralCacheKey k = new ReferralCacheKey(cname, service);
114+
PrincipalName user, Ticket[] userSvcTickets, String fromRealm,
115+
String toRealm, Credentials creds) {
116+
Ticket userSvcTicket = (userSvcTickets != null ?
117+
userSvcTickets[0] : null);
118+
ReferralCacheKey k = new ReferralCacheKey(cname, service,
119+
user, userSvcTicket);
99120
pruneExpired(k);
100121
if (creds.getEndTime().before(new Date())) {
101122
return;
@@ -125,11 +146,16 @@ static synchronized void put(PrincipalName cname, PrincipalName service,
125146

126147
/*
127148
* Obtain a referral entry from the cache given a client principal,
128-
* service principal and a source KDC realm.
149+
* a service principal, a user principal (S4U2Self only), a client
150+
* service ticket (S4U2Proxy only) and a source KDC realm.
129151
*/
130152
static synchronized ReferralCacheEntry get(PrincipalName cname,
131-
PrincipalName service, String fromRealm) {
132-
ReferralCacheKey k = new ReferralCacheKey(cname, service);
153+
PrincipalName service, PrincipalName user,
154+
Ticket[] userSvcTickets, String fromRealm) {
155+
Ticket userSvcTicket = (userSvcTickets != null ?
156+
userSvcTickets[0] : null);
157+
ReferralCacheKey k = new ReferralCacheKey(cname, service,
158+
user, userSvcTicket);
133159
pruneExpired(k);
134160
Map<String, ReferralCacheEntry> entries = referralsMap.get(k);
135161
if (entries != null) {

test/jdk/sun/security/krb5/auto/ReferralsTest.java

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2019, 2020, Red Hat, Inc.
2+
* Copyright (c) 2019, 2021, Red Hat, Inc.
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
@@ -287,6 +287,16 @@ private static void testDelegation() throws Exception {
287287
* on different realms.
288288
*/
289289
private static void testImpersonation() throws Exception {
290+
testImpersonationSingle();
291+
292+
// Try a second time to force the use of the Referrals Cache.
293+
// During this execution, the referral ticket from RABBIT.HOLE
294+
// to DEV.RABBIT.HOLE (upon the initial S4U2Self message) will
295+
// be obtained from the Cache.
296+
testImpersonationSingle();
297+
}
298+
299+
private static void testImpersonationSingle() throws Exception {
290300
Context s = Context.fromUserPass(serviceKDC2Name, password, true);
291301
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
292302
GSSName impName = s.impersonate(userKDC1Name).cred().getName();
@@ -306,6 +316,16 @@ private static void testImpersonation() throws Exception {
306316
* because the server and the backend are on different realms.
307317
*/
308318
private static void testDelegationWithReferrals() throws Exception {
319+
testDelegationWithReferralsSingle();
320+
321+
// Try a second time to force the use of the Referrals Cache.
322+
// During this execution, the referral ticket from RABBIT.HOLE
323+
// to DEV.RABBIT.HOLE (upon the initial S4U2Proxy message) will
324+
// be obtained from the Cache.
325+
testDelegationWithReferralsSingle();
326+
}
327+
328+
private static void testDelegationWithReferralsSingle() throws Exception {
309329
Context c = Context.fromUserPass(userKDC1Name, password, false);
310330
c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID);
311331
Context s = Context.fromUserPass(serviceKDC2Name, password, true);

0 commit comments

Comments
 (0)