8227437: S4U2proxy cannot continue because server's TGT cannot be found

Reviewed-by: weijun
This commit is contained in:
Martin Balao 2019-07-17 12:26:56 -03:00
parent 5fd772a6f1
commit 3cd50f2666
23 changed files with 466 additions and 149 deletions

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2019, 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
@ -49,7 +49,9 @@ public class Credentials {
Ticket ticket;
PrincipalName client;
PrincipalName clientAlias;
PrincipalName server;
PrincipalName serverAlias;
EncryptionKey key;
TicketFlags flags;
KerberosTime authTime;
@ -69,7 +71,9 @@ public class Credentials {
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_client_alias,
PrincipalName new_server,
PrincipalName new_server_alias,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
@ -78,14 +82,17 @@ public class Credentials {
KerberosTime renewTill,
HostAddresses cAddr,
AuthorizationData authzData) {
this(new_ticket, new_client, new_server, new_key, new_flags,
authTime, new_startTime, new_endTime, renewTill, cAddr);
this(new_ticket, new_client, new_client_alias, new_server,
new_server_alias, new_key, new_flags, authTime,
new_startTime, new_endTime, renewTill, cAddr);
this.authzData = authzData;
}
public Credentials(Ticket new_ticket,
PrincipalName new_client,
PrincipalName new_client_alias,
PrincipalName new_server,
PrincipalName new_server_alias,
EncryptionKey new_key,
TicketFlags new_flags,
KerberosTime authTime,
@ -95,7 +102,9 @@ public class Credentials {
HostAddresses cAddr) {
ticket = new_ticket;
client = new_client;
clientAlias = new_client_alias;
server = new_server;
serverAlias = new_server_alias;
key = new_key;
flags = new_flags;
this.authTime = authTime;
@ -107,7 +116,9 @@ public class Credentials {
public Credentials(byte[] encoding,
String client,
String clientAlias,
String server,
String serverAlias,
byte[] keyBytes,
int keyType,
boolean[] flags,
@ -118,7 +129,11 @@ public class Credentials {
InetAddress[] cAddrs) throws KrbException, IOException {
this(new Ticket(encoding),
new PrincipalName(client, PrincipalName.KRB_NT_PRINCIPAL),
(clientAlias == null? null : new PrincipalName(clientAlias,
PrincipalName.KRB_NT_PRINCIPAL)),
new PrincipalName(server, PrincipalName.KRB_NT_SRV_INST),
(serverAlias == null? null : new PrincipalName(serverAlias,
PrincipalName.KRB_NT_SRV_INST)),
new EncryptionKey(keyType, keyBytes),
(flags == null? null: new TicketFlags(flags)),
(authTime == null? null: new KerberosTime(authTime)),
@ -143,10 +158,18 @@ public class Credentials {
return client;
}
public final PrincipalName getClientAlias() {
return clientAlias;
}
public final PrincipalName getServer() {
return server;
}
public final PrincipalName getServerAlias() {
return serverAlias;
}
public final EncryptionKey getSessionKey() {
return key;
}
@ -262,6 +285,7 @@ public class Credentials {
return new KrbTgsReq(options,
this,
server,
serverAlias,
null, // from
null, // till
null, // rtime
@ -484,7 +508,11 @@ public class Credentials {
public static void printDebug(Credentials c) {
System.out.println(">>> DEBUG: ----Credentials----");
System.out.println("\tclient: " + c.client.toString());
if (c.clientAlias != null)
System.out.println("\tclient alias: " + c.clientAlias.toString());
System.out.println("\tserver: " + c.server.toString());
if (c.serverAlias != null)
System.out.println("\tserver alias: " + c.serverAlias.toString());
System.out.println("\tticket: sname: " + c.ticket.sname.toString());
if (c.startTime != null) {
System.out.println("\tstartTime: " + c.startTime.getTime());
@ -512,7 +540,11 @@ public class Credentials {
public String toString() {
StringBuilder sb = new StringBuilder("Credentials:");
sb.append( "\n client=").append(client);
if (clientAlias != null)
sb.append( "\n clientAlias=").append(clientAlias);
sb.append( "\n server=").append(server);
if (serverAlias != null)
sb.append( "\n serverAlias=").append(serverAlias);
if (authTime != null) {
sb.append("\n authTime=").append(authTime);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2011, 2019, 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
@ -25,6 +25,8 @@
package sun.security.krb5;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KeyTab;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.PrincipalName;
@ -39,4 +41,12 @@ public interface JavaxSecurityAuthKerberosAccess {
*/
public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot(
KeyTab ktab);
public KerberosPrincipal kerberosTicketGetClientAlias(KerberosTicket t);
public void kerberosTicketSetClientAlias(KerberosTicket t, KerberosPrincipal a);
public KerberosPrincipal kerberosTicketGetServerAlias(KerberosTicket t);
public void kerberosTicketSetServerAlias(KerberosTicket t, KerberosPrincipal a);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2019, 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
@ -363,7 +363,9 @@ public class KrbApReq {
creds = new Credentials(
apReqMessg.ticket,
authenticator.cname,
null,
apReqMessg.ticket.sname,
null,
enc_ticketPart.key,
enc_ticketPart.flags,
enc_ticketPart.authtime,

View file

@ -118,7 +118,7 @@ class KrbAsRep extends KrbKdcRep {
"Cannot find key for type/kvno to decrypt AS REP - " +
EType.toString(encPartKeyType) + "/" + encPartKvno);
}
decrypt(dkey, asReq);
decrypt(dkey, asReq, cname);
}
/**
@ -136,7 +136,7 @@ class KrbAsRep extends KrbKdcRep {
password,
encPartKeyType,
PAData.getSaltAndParams(encPartKeyType, rep.pAData));
decrypt(dkey, asReq);
decrypt(dkey, asReq, cname);
}
/**
@ -144,7 +144,8 @@ class KrbAsRep extends KrbKdcRep {
* @param dkey the decryption key to use
* @param asReq the original AS-REQ sent, used to validate AS-REP
*/
private void decrypt(EncryptionKey dkey, KrbAsReq asReq)
private void decrypt(EncryptionKey dkey, KrbAsReq asReq,
PrincipalName cname)
throws KrbException, Asn1Exception, IOException {
byte[] enc_as_rep_bytes = rep.encPart.decrypt(dkey,
KeyUsage.KU_ENC_AS_REP_PART);
@ -157,10 +158,16 @@ class KrbAsRep extends KrbKdcRep {
ASReq req = asReq.getMessage();
check(true, req, rep, dkey);
PrincipalName clientAlias = cname;
if (clientAlias.equals(rep.cname))
clientAlias = null;
creds = new Credentials(
rep.ticket,
rep.cname,
clientAlias,
enc_part.sname,
null, // No server alias expected in a TGT
enc_part.key,
enc_part.flags,
enc_part.authtime,

View file

@ -68,6 +68,7 @@ public final class KrbAsReqBuilder {
// Common data for AS-REQ fields
private KDCOptions options;
private PrincipalName cname;
private PrincipalName refCname; // May be changed by referrals
private PrincipalName sname;
private KerberosTime from;
private KerberosTime till;
@ -100,6 +101,7 @@ public final class KrbAsReqBuilder {
private void init(PrincipalName cname)
throws KrbException {
this.cname = cname;
this.refCname = cname;
state = State.INIT;
}
@ -284,7 +286,7 @@ public final class KrbAsReqBuilder {
}
return new KrbAsReq(key,
options,
cname,
refCname,
sname,
from,
till,
@ -334,7 +336,7 @@ public final class KrbAsReqBuilder {
ReferralsState referralsState = new ReferralsState();
while (true) {
if (referralsState.refreshComm()) {
comm = new KdcComm(cname.getRealmAsString());
comm = new KdcComm(refCname.getRealmAsString());
}
try {
req = build(pakey, referralsState);
@ -384,7 +386,7 @@ public final class KrbAsReqBuilder {
ReferralsState() throws KrbException {
if (Config.DISABLE_REFERRALS) {
if (cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
if (refCname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) {
throw new KrbException("NT-ENTERPRISE principals only allowed" +
" when referrals are enabled.");
}
@ -402,15 +404,15 @@ public final class KrbAsReqBuilder {
if (req.getMessage().reqBody.kdcOptions.get(KDCOptions.CANONICALIZE) &&
referredRealm != null && referredRealm.toString().length() > 0 &&
count < Config.MAX_REFERRALS) {
cname = new PrincipalName(cname.getNameType(),
cname.getNameStrings(), referredRealm);
refCname = new PrincipalName(refCname.getNameType(),
refCname.getNameStrings(), referredRealm);
refreshComm = true;
count++;
return true;
}
}
if (count < Config.MAX_REFERRALS &&
cname.getNameType() != PrincipalName.KRB_NT_ENTERPRISE) {
refCname.getNameType() != PrincipalName.KRB_NT_ENTERPRISE) {
// Server may raise an error if CANONICALIZE is true.
// Try CANONICALIZE false.
enabled = false;

View file

@ -1,5 +1,5 @@
/*
* Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2000, 2019, 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
@ -76,7 +76,7 @@ public class KrbCred {
options.set(KDCOptions.FORWARDABLE, true);
KrbTgsReq tgsReq = new KrbTgsReq(options, tgt, tgService,
null, null, null, null,
null, null, null, null, null,
null, // No easy way to get addresses right
null, null, null);
credMessg = createMessage(tgsReq.sendAndGetCreds(), key);
@ -152,7 +152,7 @@ public class KrbCred {
+ " endtime=" + endtime
+ "renewTill=" + renewTill);
}
creds = new Credentials(ticket, pname, sname, credInfoKey,
creds = new Credentials(ticket, pname, null, sname, null, credInfoKey,
flags, authtime, starttime, endtime, renewTill, caddr);
}

View file

@ -86,9 +86,20 @@ public class KrbTgsRep extends KrbKdcRep {
check(false, req, rep, tgsReq.tgsReqKey);
PrincipalName serverAlias = tgsReq.getServerAlias();
if (serverAlias != null) {
PrincipalName repSname = enc_part.sname;
if (serverAlias.equals(repSname) ||
isReferralSname(repSname)) {
serverAlias = null;
}
}
this.creds = new Credentials(rep.ticket,
rep.cname,
tgsReq.getClientAlias(),
enc_part.sname,
serverAlias,
enc_part.key,
enc_part.flags,
enc_part.authtime,
@ -111,4 +122,16 @@ public class KrbTgsRep extends KrbKdcRep {
sun.security.krb5.internal.ccache.Credentials setCredentials() {
return new sun.security.krb5.internal.ccache.Credentials(rep, secondTicket);
}
private static boolean isReferralSname(PrincipalName sname) {
if (sname != null) {
String[] snameStrings = sname.getNameStrings();
if (snameStrings.length == 2 &&
snameStrings[0].equals(
PrincipalName.TGS_DEFAULT_SRV_NAME)) {
return true;
}
}
return false;
}
}

View file

@ -45,7 +45,9 @@ import java.util.Arrays;
public class KrbTgsReq {
private PrincipalName princName;
private PrincipalName clientAlias;
private PrincipalName servName;
private PrincipalName serverAlias;
private TGSReq tgsReqMessg;
private KerberosTime ctime;
private Ticket secondTicket = null;
@ -59,13 +61,16 @@ public class KrbTgsReq {
// Used in CredentialsUtil
public KrbTgsReq(KDCOptions options, Credentials asCreds,
PrincipalName cname, PrincipalName sname,
PrincipalName cname, PrincipalName clientAlias,
PrincipalName sname, PrincipalName serverAlias,
Ticket[] additionalTickets, PAData[] extraPAs)
throws KrbException, IOException {
this(options,
asCreds,
cname,
clientAlias,
sname,
serverAlias,
null, // KerberosTime from
null, // KerberosTime till
null, // KerberosTime rtime
@ -82,6 +87,7 @@ public class KrbTgsReq {
KDCOptions options,
Credentials asCreds,
PrincipalName sname,
PrincipalName serverAlias,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
@ -90,16 +96,18 @@ public class KrbTgsReq {
AuthorizationData authorizationData,
Ticket[] additionalTickets,
EncryptionKey subKey) throws KrbException, IOException {
this(options, asCreds, asCreds.getClient(), sname,
from, till, rtime, eTypes, addresses,
authorizationData, additionalTickets, subKey, null);
this(options, asCreds, asCreds.getClient(), asCreds.getClientAlias(),
sname, serverAlias, from, till, rtime, eTypes,
addresses, authorizationData, additionalTickets, subKey, null);
}
private KrbTgsReq(
KDCOptions options,
Credentials asCreds,
PrincipalName cname,
PrincipalName clientAlias,
PrincipalName sname,
PrincipalName serverAlias,
KerberosTime from,
KerberosTime till,
KerberosTime rtime,
@ -111,7 +119,9 @@ public class KrbTgsReq {
PAData[] extraPAs) throws KrbException, IOException {
princName = cname;
this.clientAlias = clientAlias;
servName = sname;
this.serverAlias = serverAlias;
ctime = KerberosTime.now();
// check if they are valid arguments. The optional fields
@ -365,6 +375,14 @@ public class KrbTgsReq {
return secondTicket;
}
PrincipalName getClientAlias() {
return clientAlias;
}
PrincipalName getServerAlias() {
return serverAlias;
}
private static void debug(String message) {
// System.err.println(">>> KrbTgsReq: " + message);
}

View file

@ -564,7 +564,9 @@ public class PrincipalName implements Cloneable {
for (int i = 0; i < nameStrings.length; i++) {
if (i > 0)
str.append("/");
str.append(nameStrings[i]);
String n = nameStrings[i];
n = n.replace("@", "\\@");
str.append(n);
}
str.append("@");
str.append(nameRealm.toString());

View file

@ -284,8 +284,9 @@ public class CredentialsUtil {
// Try CANONICALIZE false.
}
}
return serviceCredsSingle(options, asCreds,
cname, sname, additionalTickets, extraPAs);
return serviceCredsSingle(options, asCreds, cname,
asCreds.getClientAlias(), sname, sname, additionalTickets,
extraPAs);
}
/*
@ -300,26 +301,29 @@ public class CredentialsUtil {
options = new KDCOptions(options.toBooleanArray());
options.set(KDCOptions.CANONICALIZE, true);
PrincipalName cSname = sname;
PrincipalName refSname = sname; // May change with referrals
Credentials creds = null;
boolean isReferral = false;
List<String> referrals = new LinkedList<>();
PrincipalName clientAlias = asCreds.getClientAlias();
while (referrals.size() <= Config.MAX_REFERRALS) {
ReferralsCache.ReferralCacheEntry ref =
ReferralsCache.get(sname, cSname.getRealmString());
ReferralsCache.get(cname, sname, refSname.getRealmString());
String toRealm = null;
if (ref == null) {
creds = serviceCredsSingle(options, asCreds,
cname, cSname, additionalTickets, extraPAs);
creds = serviceCredsSingle(options, asCreds, cname,
clientAlias, refSname, cSname, additionalTickets,
extraPAs);
PrincipalName server = creds.getServer();
if (!cSname.equals(server)) {
if (!refSname.equals(server)) {
String[] serverNameStrings = server.getNameStrings();
if (serverNameStrings.length == 2 &&
serverNameStrings[0].equals(
PrincipalName.TGS_DEFAULT_SRV_NAME) &&
!cSname.getRealmAsString().equals(serverNameStrings[1])) {
!refSname.getRealmAsString().equals(serverNameStrings[1])) {
// Server Name (sname) has the following format:
// krbtgt/TO-REALM.COM@FROM-REALM.COM
ReferralsCache.put(sname, server.getRealmString(),
ReferralsCache.put(cname, sname, server.getRealmString(),
serverNameStrings[1], creds);
toRealm = serverNameStrings[1];
isReferral = true;
@ -336,8 +340,8 @@ public class CredentialsUtil {
// Referrals loop detected
return null;
}
cSname = new PrincipalName(cSname.getNameString(),
cSname.getNameType(), toRealm);
refSname = new PrincipalName(refSname.getNameString(),
refSname.getNameType(), toRealm);
referrals.add(toRealm);
isReferral = false;
continue;
@ -356,14 +360,15 @@ public class CredentialsUtil {
*/
private static Credentials serviceCredsSingle(
KDCOptions options, Credentials asCreds,
PrincipalName cname, PrincipalName sname,
PrincipalName cname, PrincipalName clientAlias,
PrincipalName refSname, PrincipalName sname,
Ticket[] additionalTickets, PAData[] extraPAs)
throws KrbException, IOException {
Credentials theCreds = null;
boolean[] okAsDelegate = new boolean[]{true};
String[] serverAsCredsNames = asCreds.getServer().getNameStrings();
String tgtRealm = serverAsCredsNames[1];
String serviceRealm = sname.getRealmString();
String serviceRealm = refSname.getRealmString();
if (!serviceRealm.equals(tgtRealm)) {
// This is a cross-realm service request
if (DEBUG) {
@ -390,8 +395,8 @@ public class CredentialsUtil {
System.out.println(">>> Credentials serviceCredsSingle:" +
" same realm");
}
KrbTgsReq req = new KrbTgsReq(options, asCreds,
cname, sname, additionalTickets, extraPAs);
KrbTgsReq req = new KrbTgsReq(options, asCreds, cname, clientAlias,
refSname, sname, additionalTickets, extraPAs);
theCreds = req.sendAndGetCreds();
if (theCreds != null) {
if (DEBUG) {

View file

@ -139,7 +139,7 @@ public class KRBError implements java.io.Serializable {
sTime = new_sTime;
suSec = new_suSec;
errorCode = new_errorCode;
crealm = new_cname.getRealm();
crealm = new_cname != null ? new_cname.getRealm() : null;
cname = new_cname;
sname = new_sname;
eText = new_eText;
@ -168,7 +168,7 @@ public class KRBError implements java.io.Serializable {
sTime = new_sTime;
suSec = new_suSec;
errorCode = new_errorCode;
crealm = new_cname.getRealm();
crealm = new_cname != null ? new_cname.getRealm() : null;
cname = new_cname;
sname = new_sname;
eText = new_eText;

View file

@ -45,8 +45,27 @@ import sun.security.krb5.PrincipalName;
*/
final class ReferralsCache {
private static Map<PrincipalName, Map<String, ReferralCacheEntry>> referralsMap =
new HashMap<>();
private static Map<ReferralCacheKey, Map<String, ReferralCacheEntry>>
referralsMap = new HashMap<>();
static private final class ReferralCacheKey {
private PrincipalName cname;
private PrincipalName sname;
ReferralCacheKey (PrincipalName cname, PrincipalName sname) {
this.cname = cname;
this.sname = sname;
}
public boolean equals(Object other) {
if (!(other instanceof ReferralCacheKey))
return false;
ReferralCacheKey that = (ReferralCacheKey)other;
return cname.equals(that.cname) &&
sname.equals(that.sname);
}
public int hashCode() {
return cname.hashCode() + sname.hashCode();
}
}
static final class ReferralCacheEntry {
private final Credentials creds;
@ -64,8 +83,9 @@ final class ReferralsCache {
}
/*
* Add a new referral entry to the cache, including: service principal,
* source KDC realm, destination KDC realm and referral TGT.
* Add a new referral entry to the cache, including: client principal,
* service principal, source KDC realm, destination KDC realm and
* referral TGT.
*
* If a loop is generated when adding the new referral, the first hop is
* automatically removed. For example, let's assume that adding a
@ -73,16 +93,17 @@ final class ReferralsCache {
* REALM-1.COM -> REALM-2.COM -> REALM-3.COM -> REALM-1.COM. Then,
* REALM-1.COM -> REALM-2.COM referral entry is removed from the cache.
*/
static synchronized void put(PrincipalName service,
static synchronized void put(PrincipalName cname, PrincipalName service,
String fromRealm, String toRealm, Credentials creds) {
pruneExpired(service);
ReferralCacheKey k = new ReferralCacheKey(cname, service);
pruneExpired(k);
if (creds.getEndTime().before(new Date())) {
return;
}
Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
Map<String, ReferralCacheEntry> entries = referralsMap.get(k);
if (entries == null) {
entries = new HashMap<String, ReferralCacheEntry>();
referralsMap.put(service, entries);
referralsMap.put(k, entries);
}
entries.remove(fromRealm);
ReferralCacheEntry newEntry = new ReferralCacheEntry(creds, toRealm);
@ -103,13 +124,14 @@ final class ReferralsCache {
}
/*
* Obtain a referral entry from the cache given a service principal and a
* source KDC realm.
* Obtain a referral entry from the cache given a client principal,
* service principal and a source KDC realm.
*/
static synchronized ReferralCacheEntry get(PrincipalName service,
String fromRealm) {
pruneExpired(service);
Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
static synchronized ReferralCacheEntry get(PrincipalName cname,
PrincipalName service, String fromRealm) {
ReferralCacheKey k = new ReferralCacheKey(cname, service);
pruneExpired(k);
Map<String, ReferralCacheEntry> entries = referralsMap.get(k);
if (entries != null) {
ReferralCacheEntry toRef = entries.get(fromRealm);
if (toRef != null) {
@ -122,9 +144,9 @@ final class ReferralsCache {
/*
* Remove referral entries from the cache when referral TGTs expire.
*/
private static void pruneExpired(PrincipalName service) {
private static void pruneExpired(ReferralCacheKey k) {
Date now = new Date();
Map<String, ReferralCacheEntry> entries = referralsMap.get(service);
Map<String, ReferralCacheEntry> entries = referralsMap.get(k);
if (entries != null) {
for (Entry<String, ReferralCacheEntry> mapEntry :
entries.entrySet()) {

View file

@ -180,8 +180,9 @@ public class Credentials {
// is most likely to be the one in Authenticator in PA-TGS-REQ encoded
// in TGS-REQ, therefore only stored with a service ticket. Currently
// in Java, we only reads TGTs.
return new sun.security.krb5.Credentials(ticket,
cname, sname, key, flags, authtime, starttime, endtime, renewTill, caddr);
return new sun.security.krb5.Credentials(ticket, cname, null, sname,
null, key, flags, authtime, starttime, endtime, renewTill,
caddr);
}
public KerberosTime getStartTime() {