8270137: Kerberos Credential Retrieval from Cache not Working in Cross-Realm Setup
Reviewed-by: weijun
This commit is contained in:
parent
35b399aca8
commit
67869b491a
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2001, 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -53,22 +53,22 @@ public class CredentialsUtil {
|
||||
|
||||
/**
|
||||
* Used by a middle server to acquire credentials on behalf of a
|
||||
* client to itself using the S4U2self extension.
|
||||
* @param client the client to impersonate
|
||||
* user to itself using the S4U2self extension.
|
||||
* @param user the user to impersonate
|
||||
* @param ccreds the TGT of the middle service
|
||||
* @return the new creds (cname=client, sname=middle)
|
||||
* @return the new creds (cname=user, sname=middle)
|
||||
*/
|
||||
public static Credentials acquireS4U2selfCreds(PrincipalName client,
|
||||
public static Credentials acquireS4U2selfCreds(PrincipalName user,
|
||||
Credentials ccreds) throws KrbException, IOException {
|
||||
if (!ccreds.isForwardable()) {
|
||||
throw new KrbException("S4U2self needs a FORWARDABLE ticket");
|
||||
}
|
||||
PrincipalName sname = ccreds.getClient();
|
||||
String uRealm = client.getRealmString();
|
||||
String uRealm = user.getRealmString();
|
||||
String localRealm = ccreds.getClient().getRealmString();
|
||||
if (!uRealm.equals(localRealm)) {
|
||||
// Referrals will be required because the middle service
|
||||
// and the client impersonated are on different realms.
|
||||
// and the user impersonated are on different realms.
|
||||
if (Config.DISABLE_REFERRALS) {
|
||||
throw new KrbException("Cross-realm S4U2Self request not" +
|
||||
" possible when referrals are disabled.");
|
||||
@ -87,10 +87,10 @@ public class CredentialsUtil {
|
||||
}
|
||||
Credentials creds = serviceCreds(
|
||||
KDCOptions.with(KDCOptions.FORWARDABLE),
|
||||
ccreds, ccreds.getClient(), sname, null,
|
||||
new PAData[] {
|
||||
ccreds, ccreds.getClient(), sname, user,
|
||||
null, new PAData[] {
|
||||
new PAData(Krb5.PA_FOR_USER,
|
||||
new PAForUserEnc(client,
|
||||
new PAForUserEnc(user,
|
||||
ccreds.getSessionKey()).asn1Encode()),
|
||||
new PAData(Krb5.PA_PAC_OPTIONS,
|
||||
new PaPacOptions()
|
||||
@ -98,7 +98,7 @@ public class CredentialsUtil {
|
||||
.setClaims(true)
|
||||
.asn1Encode())
|
||||
}, S4U2Type.SELF);
|
||||
if (!creds.getClient().equals(client)) {
|
||||
if (!creds.getClient().equals(user)) {
|
||||
throw new KrbException("S4U2self request not honored by KDC");
|
||||
}
|
||||
if (!creds.isForwardable()) {
|
||||
@ -136,7 +136,7 @@ public class CredentialsUtil {
|
||||
}
|
||||
Credentials creds = serviceCreds(KDCOptions.with(
|
||||
KDCOptions.CNAME_IN_ADDL_TKT, KDCOptions.FORWARDABLE),
|
||||
ccreds, ccreds.getClient(), backendPrincipal,
|
||||
ccreds, ccreds.getClient(), backendPrincipal, null,
|
||||
new Ticket[] {second}, new PAData[] {
|
||||
new PAData(Krb5.PA_PAC_OPTIONS,
|
||||
new PaPacOptions()
|
||||
@ -313,7 +313,7 @@ public class CredentialsUtil {
|
||||
throws KrbException, IOException {
|
||||
return serviceCreds(new KDCOptions(), ccreds,
|
||||
ccreds.getClient(), service, null, null,
|
||||
S4U2Type.NONE);
|
||||
null, S4U2Type.NONE);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -325,13 +325,13 @@ public class CredentialsUtil {
|
||||
private static Credentials serviceCreds(
|
||||
KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName sname,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs,
|
||||
S4U2Type s4u2Type)
|
||||
PrincipalName user, Ticket[] additionalTickets,
|
||||
PAData[] extraPAs, S4U2Type s4u2Type)
|
||||
throws KrbException, IOException {
|
||||
if (!Config.DISABLE_REFERRALS) {
|
||||
try {
|
||||
return serviceCredsReferrals(options, asCreds, cname, sname,
|
||||
s4u2Type, additionalTickets, extraPAs);
|
||||
s4u2Type, user, additionalTickets, extraPAs);
|
||||
} catch (KrbException e) {
|
||||
// Server may raise an error if CANONICALIZE is true.
|
||||
// Try CANONICALIZE false.
|
||||
@ -339,7 +339,7 @@ public class CredentialsUtil {
|
||||
}
|
||||
return serviceCredsSingle(options, asCreds, cname,
|
||||
asCreds.getClientAlias(), sname, sname, s4u2Type,
|
||||
additionalTickets, extraPAs);
|
||||
user, additionalTickets, extraPAs);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -349,8 +349,8 @@ public class CredentialsUtil {
|
||||
private static Credentials serviceCredsReferrals(
|
||||
KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName sname,
|
||||
S4U2Type s4u2Type, Ticket[] additionalTickets,
|
||||
PAData[] extraPAs)
|
||||
S4U2Type s4u2Type, PrincipalName user,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
options = new KDCOptions(options.toBooleanArray());
|
||||
options.set(KDCOptions.CANONICALIZE, true);
|
||||
@ -362,12 +362,13 @@ public class CredentialsUtil {
|
||||
PrincipalName clientAlias = asCreds.getClientAlias();
|
||||
while (referrals.size() <= Config.MAX_REFERRALS) {
|
||||
ReferralsCache.ReferralCacheEntry ref =
|
||||
ReferralsCache.get(cname, sname, refSname.getRealmString());
|
||||
ReferralsCache.get(cname, sname, user,
|
||||
additionalTickets, refSname.getRealmString());
|
||||
String toRealm = null;
|
||||
if (ref == null) {
|
||||
creds = serviceCredsSingle(options, asCreds, cname,
|
||||
clientAlias, refSname, cSname, s4u2Type,
|
||||
additionalTickets, extraPAs);
|
||||
user, additionalTickets, extraPAs);
|
||||
PrincipalName server = creds.getServer();
|
||||
if (!refSname.equals(server)) {
|
||||
String[] serverNameStrings = server.getNameStrings();
|
||||
@ -378,15 +379,9 @@ public class CredentialsUtil {
|
||||
serverNameStrings[1])) {
|
||||
// Server Name (sname) has the following format:
|
||||
// krbtgt/TO-REALM.COM@FROM-REALM.COM
|
||||
if (s4u2Type == S4U2Type.NONE) {
|
||||
// Do not store S4U2Self or S4U2Proxy referral
|
||||
// TGTs in the cache. Caching such tickets is not
|
||||
// defined in MS-SFU and may cause unexpected
|
||||
// results when using them in a different context.
|
||||
ReferralsCache.put(cname, sname,
|
||||
server.getRealmString(),
|
||||
serverNameStrings[1], creds);
|
||||
}
|
||||
ReferralsCache.put(cname, sname, user,
|
||||
additionalTickets, server.getRealmString(),
|
||||
serverNameStrings[1], creds);
|
||||
toRealm = serverNameStrings[1];
|
||||
isReferral = true;
|
||||
}
|
||||
@ -411,7 +406,7 @@ public class CredentialsUtil {
|
||||
}
|
||||
additionalTickets[0] = credsInOut[1].getTicket();
|
||||
} else if (s4u2Type == S4U2Type.SELF) {
|
||||
handleS4U2SelfReferral(extraPAs, asCreds, creds);
|
||||
handleS4U2SelfReferral(extraPAs, user, creds);
|
||||
}
|
||||
if (referrals.contains(toRealm)) {
|
||||
// Referrals loop detected
|
||||
@ -440,8 +435,8 @@ public class CredentialsUtil {
|
||||
KDCOptions options, Credentials asCreds,
|
||||
PrincipalName cname, PrincipalName clientAlias,
|
||||
PrincipalName refSname, PrincipalName sname,
|
||||
S4U2Type s4u2Type, Ticket[] additionalTickets,
|
||||
PAData[] extraPAs)
|
||||
S4U2Type s4u2Type, PrincipalName user,
|
||||
Ticket[] additionalTickets, PAData[] extraPAs)
|
||||
throws KrbException, IOException {
|
||||
Credentials theCreds = null;
|
||||
boolean[] okAsDelegate = new boolean[]{true};
|
||||
@ -469,7 +464,7 @@ public class CredentialsUtil {
|
||||
Credentials.printDebug(newTgt);
|
||||
}
|
||||
if (s4u2Type == S4U2Type.SELF) {
|
||||
handleS4U2SelfReferral(extraPAs, asCreds, newTgt);
|
||||
handleS4U2SelfReferral(extraPAs, user, newTgt);
|
||||
}
|
||||
asCreds = newTgt;
|
||||
cname = asCreds.getClient();
|
||||
@ -498,7 +493,7 @@ public class CredentialsUtil {
|
||||
* different realm or when using a referral TGT.
|
||||
*/
|
||||
private static void handleS4U2SelfReferral(PAData[] pas,
|
||||
Credentials oldCeds, Credentials newCreds)
|
||||
PrincipalName user, Credentials newCreds)
|
||||
throws Asn1Exception, KrbException, IOException {
|
||||
if (DEBUG) {
|
||||
System.out.println(">>> Handling S4U2Self referral");
|
||||
@ -506,11 +501,8 @@ public class CredentialsUtil {
|
||||
for (int i = 0; i < pas.length; i++) {
|
||||
PAData pa = pas[i];
|
||||
if (pa.getType() == Krb5.PA_FOR_USER) {
|
||||
PAForUserEnc paForUser = new PAForUserEnc(
|
||||
new DerValue(pa.getValue()),
|
||||
oldCeds.getSessionKey());
|
||||
pas[i] = new PAData(Krb5.PA_FOR_USER,
|
||||
new PAForUserEnc(paForUser.getName(),
|
||||
new PAForUserEnc(user,
|
||||
newCreds.getSessionKey()).asn1Encode());
|
||||
break;
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Red Hat, Inc.
|
||||
* Copyright (c) 2019, 2021, Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -25,12 +25,14 @@
|
||||
|
||||
package sun.security.krb5.internal;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
|
||||
import sun.security.krb5.Credentials;
|
||||
import sun.security.krb5.PrincipalName;
|
||||
@ -51,19 +53,33 @@ final class ReferralsCache {
|
||||
private static final class ReferralCacheKey {
|
||||
private PrincipalName cname;
|
||||
private PrincipalName sname;
|
||||
ReferralCacheKey (PrincipalName cname, PrincipalName sname) {
|
||||
private PrincipalName user; // S4U2Self only
|
||||
private byte[] userSvcTicketEnc; // S4U2Proxy only
|
||||
ReferralCacheKey (PrincipalName cname, PrincipalName sname,
|
||||
PrincipalName user, Ticket userSvcTicket) {
|
||||
this.cname = cname;
|
||||
this.sname = sname;
|
||||
this.user = user;
|
||||
if (userSvcTicket != null && userSvcTicket.encPart != null) {
|
||||
byte[] userSvcTicketEnc = userSvcTicket.encPart.getBytes();
|
||||
if (userSvcTicketEnc.length > 0) {
|
||||
this.userSvcTicketEnc = userSvcTicketEnc;
|
||||
}
|
||||
}
|
||||
}
|
||||
public boolean equals(Object other) {
|
||||
if (!(other instanceof ReferralCacheKey))
|
||||
return false;
|
||||
ReferralCacheKey that = (ReferralCacheKey)other;
|
||||
return cname.equals(that.cname) &&
|
||||
sname.equals(that.sname);
|
||||
sname.equals(that.sname) &&
|
||||
Objects.equals(user, that.user) &&
|
||||
Arrays.equals(userSvcTicketEnc, that.userSvcTicketEnc);
|
||||
}
|
||||
public int hashCode() {
|
||||
return cname.hashCode() + sname.hashCode();
|
||||
return cname.hashCode() + sname.hashCode() +
|
||||
Objects.hashCode(user) +
|
||||
Arrays.hashCode(userSvcTicketEnc);
|
||||
}
|
||||
}
|
||||
|
||||
@ -84,7 +100,8 @@ final class ReferralsCache {
|
||||
|
||||
/*
|
||||
* Add a new referral entry to the cache, including: client principal,
|
||||
* service principal, source KDC realm, destination KDC realm and
|
||||
* service principal, user principal (S4U2Self only), client service
|
||||
* ticket (S4U2Proxy only), source KDC realm, destination KDC realm and
|
||||
* referral TGT.
|
||||
*
|
||||
* If a loop is generated when adding the new referral, the first hop is
|
||||
@ -94,8 +111,12 @@ final class ReferralsCache {
|
||||
* REALM-1.COM -> REALM-2.COM referral entry is removed from the cache.
|
||||
*/
|
||||
static synchronized void put(PrincipalName cname, PrincipalName service,
|
||||
String fromRealm, String toRealm, Credentials creds) {
|
||||
ReferralCacheKey k = new ReferralCacheKey(cname, service);
|
||||
PrincipalName user, Ticket[] userSvcTickets, String fromRealm,
|
||||
String toRealm, Credentials creds) {
|
||||
Ticket userSvcTicket = (userSvcTickets != null ?
|
||||
userSvcTickets[0] : null);
|
||||
ReferralCacheKey k = new ReferralCacheKey(cname, service,
|
||||
user, userSvcTicket);
|
||||
pruneExpired(k);
|
||||
if (creds.getEndTime().before(new Date())) {
|
||||
return;
|
||||
@ -125,11 +146,16 @@ final class ReferralsCache {
|
||||
|
||||
/*
|
||||
* Obtain a referral entry from the cache given a client principal,
|
||||
* service principal and a source KDC realm.
|
||||
* a service principal, a user principal (S4U2Self only), a client
|
||||
* service ticket (S4U2Proxy only) and a source KDC realm.
|
||||
*/
|
||||
static synchronized ReferralCacheEntry get(PrincipalName cname,
|
||||
PrincipalName service, String fromRealm) {
|
||||
ReferralCacheKey k = new ReferralCacheKey(cname, service);
|
||||
PrincipalName service, PrincipalName user,
|
||||
Ticket[] userSvcTickets, String fromRealm) {
|
||||
Ticket userSvcTicket = (userSvcTickets != null ?
|
||||
userSvcTickets[0] : null);
|
||||
ReferralCacheKey k = new ReferralCacheKey(cname, service,
|
||||
user, userSvcTicket);
|
||||
pruneExpired(k);
|
||||
Map<String, ReferralCacheEntry> entries = referralsMap.get(k);
|
||||
if (entries != null) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2019, 2020, Red Hat, Inc.
|
||||
* Copyright (c) 2019, 2021, Red Hat, Inc.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -287,6 +287,16 @@ public class ReferralsTest {
|
||||
* on different realms.
|
||||
*/
|
||||
private static void testImpersonation() throws Exception {
|
||||
testImpersonationSingle();
|
||||
|
||||
// Try a second time to force the use of the Referrals Cache.
|
||||
// During this execution, the referral ticket from RABBIT.HOLE
|
||||
// to DEV.RABBIT.HOLE (upon the initial S4U2Self message) will
|
||||
// be obtained from the Cache.
|
||||
testImpersonationSingle();
|
||||
}
|
||||
|
||||
private static void testImpersonationSingle() throws Exception {
|
||||
Context s = Context.fromUserPass(serviceKDC2Name, password, true);
|
||||
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
|
||||
GSSName impName = s.impersonate(userKDC1Name).cred().getName();
|
||||
@ -306,6 +316,16 @@ public class ReferralsTest {
|
||||
* because the server and the backend are on different realms.
|
||||
*/
|
||||
private static void testDelegationWithReferrals() throws Exception {
|
||||
testDelegationWithReferralsSingle();
|
||||
|
||||
// Try a second time to force the use of the Referrals Cache.
|
||||
// During this execution, the referral ticket from RABBIT.HOLE
|
||||
// to DEV.RABBIT.HOLE (upon the initial S4U2Proxy message) will
|
||||
// be obtained from the Cache.
|
||||
testDelegationWithReferralsSingle();
|
||||
}
|
||||
|
||||
private static void testDelegationWithReferralsSingle() throws Exception {
|
||||
Context c = Context.fromUserPass(userKDC1Name, password, false);
|
||||
c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID);
|
||||
Context s = Context.fromUserPass(serviceKDC2Name, password, true);
|
||||
|
Loading…
Reference in New Issue
Block a user