From 32e7a54234ea69804ed747bf20d9bdf802eb1dc0 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 12 Jan 2020 03:05:17 +0000 Subject: [PATCH] 8227437: S4U2proxy cannot continue because server's TGT cannot be found Reviewed-by: mbalao --- .../JavaxSecurityAuthKerberosAccessImpl.java | 16 ++ .../auth/kerberos/KerberosTicket.java | 8 + .../sun/security/jgss/krb5/Krb5Context.java | 14 +- .../jgss/krb5/Krb5InitCredential.java | 40 +++- .../sun/security/jgss/krb5/Krb5Util.java | 23 ++- .../sun/security/jgss/krb5/SubjectComber.java | 39 +++- .../sun/security/krb5/Credentials.java | 36 +++- .../krb5/JavaxSecurityAuthKerberosAccess.java | 9 + .../classes/sun/security/krb5/KrbApReq.java | 4 +- .../classes/sun/security/krb5/KrbAsRep.java | 13 +- .../sun/security/krb5/KrbAsReqBuilder.java | 14 +- .../classes/sun/security/krb5/KrbCred.java | 8 +- .../classes/sun/security/krb5/KrbTgsRep.java | 23 +++ .../classes/sun/security/krb5/KrbTgsReq.java | 26 ++- .../sun/security/krb5/PrincipalName.java | 4 +- .../krb5/internal/CredentialsUtil.java | 33 +-- .../sun/security/krb5/internal/KRBError.java | 4 +- .../krb5/internal/ReferralsCache.java | 54 +++-- .../krb5/internal/ccache/Credentials.java | 5 +- .../native/sun/security/krb5/nativeccache.c | 6 +- .../native/sun/security/krb5/NativeCreds.c | 6 +- test/sun/security/krb5/auto/KDC.java | 13 +- .../sun/security/krb5/auto/ReferralsTest.java | 194 +++++++++++++----- 23 files changed, 456 insertions(+), 136 deletions(-) diff --git a/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java b/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java index 71cc0aa50..ff399839b 100644 --- a/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java +++ b/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java @@ -33,6 +33,22 @@ class JavaxSecurityAuthKerberosAccessImpl KeyTab ktab) { return ktab.takeSnapshot(); } + + public KerberosPrincipal kerberosTicketGetClientAlias(KerberosTicket t) { + return t.clientAlias; + } + + public void kerberosTicketSetClientAlias(KerberosTicket t, KerberosPrincipal a) { + t.clientAlias = a; + } + + public KerberosPrincipal kerberosTicketGetServerAlias(KerberosTicket t) { + return t.serverAlias; + } + + public void kerberosTicketSetServerAlias(KerberosTicket t, KerberosPrincipal a) { + t.serverAlias = a; + } public KerberosTicket kerberosTicketGetProxy(KerberosTicket t) { return t.proxy; } diff --git a/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java b/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java index dab1f038a..3f29d9ba9 100644 --- a/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java +++ b/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java @@ -194,6 +194,10 @@ public class KerberosTicket implements Destroyable, Refreshable, private InetAddress[] clientAddresses; + transient KerberosPrincipal clientAlias = null; + + transient KerberosPrincipal serverAlias = null; + /** * Evidence ticket if proxy_impersonator. This field can be accessed * by KerberosSecrets. It's serialized. @@ -563,7 +567,11 @@ public class KerberosTicket implements Destroyable, Refreshable, try { krb5Creds = new sun.security.krb5.Credentials(asn1Encoding, client.toString(), + (clientAlias != null ? + clientAlias.getName() : null), server.toString(), + (serverAlias != null ? + serverAlias.getName() : null), sessionKey.getEncoded(), sessionKey.getKeyType(), flags, diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5Context.java b/src/share/classes/sun/security/jgss/krb5/Krb5Context.java index a0658d0ea..e9ee13d44 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5Context.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5Context.java @@ -713,14 +713,14 @@ class Krb5Context implements GSSContextSpi { if (subject != null && !subject.isReadOnly()) { /* - * Store the service credentials as - * javax.security.auth.kerberos.KerberosTicket in - * the Subject. We could wait till the context is - * succesfully established; however it is easier - * to do here and there is no harm indoing it here. - */ + * Store the service credentials as + * javax.security.auth.kerberos.KerberosTicket in + * the Subject. We could wait until the context is + * successfully established; however it is easier + * to do it here and there is no harm. + */ final KerberosTicket kt = - Krb5Util.credsToTicket(serviceCreds); + Krb5Util.credsToTicket(serviceCreds); AccessController.doPrivileged ( new java.security.PrivilegedAction() { public Void run() { diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java index daefbbcbd..314cabb3c 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java @@ -60,7 +60,9 @@ public class Krb5InitCredential private Krb5InitCredential(Krb5NameElement name, byte[] asn1Encoding, KerberosPrincipal client, + KerberosPrincipal clientAlias, KerberosPrincipal server, + KerberosPrincipal serverAlias, byte[] sessionKey, int keyType, boolean[] flags, @@ -81,14 +83,21 @@ public class Krb5InitCredential endTime, renewTill, clientAddresses); - + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetClientAlias(this, clientAlias); + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetServerAlias(this, serverAlias); this.name = name; try { // Cache this for later use by the sun.security.krb5 package. krb5Credentials = new Credentials(asn1Encoding, client.getName(), + (clientAlias != null ? + clientAlias.getName() : null), server.getName(), + (serverAlias != null ? + serverAlias.getName() : null), sessionKey, keyType, flags, @@ -111,7 +120,9 @@ public class Krb5InitCredential Credentials delegatedCred, byte[] asn1Encoding, KerberosPrincipal client, + KerberosPrincipal clientAlias, KerberosPrincipal server, + KerberosPrincipal serverAlias, byte[] sessionKey, int keyType, boolean[] flags, @@ -132,7 +143,10 @@ public class Krb5InitCredential endTime, renewTill, clientAddresses); - + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetClientAlias(this, clientAlias); + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetServerAlias(this, serverAlias); this.name = name; // A delegated cred does not have all fields set. So do not try to // creat new Credentials out of the delegatedCred. @@ -154,10 +168,18 @@ public class Krb5InitCredential Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL); } + KerberosPrincipal clientAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetClientAlias(tgt); + KerberosPrincipal serverAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetServerAlias(tgt); Krb5InitCredential result = new Krb5InitCredential(name, tgt.getEncoded(), tgt.getClient(), + clientAlias, tgt.getServer(), + serverAlias, tgt.getSessionKey().getEncoded(), tgt.getSessionKeyType(), tgt.getFlags(), @@ -183,10 +205,14 @@ public class Krb5InitCredential */ PrincipalName cPrinc = delegatedCred.getClient(); + PrincipalName cAPrinc = delegatedCred.getClientAlias(); PrincipalName sPrinc = delegatedCred.getServer(); + PrincipalName sAPrinc = delegatedCred.getServerAlias(); KerberosPrincipal client = null; + KerberosPrincipal clientAlias = null; KerberosPrincipal server = null; + KerberosPrincipal serverAlias = null; Krb5NameElement credName = null; @@ -197,6 +223,10 @@ public class Krb5InitCredential client = new KerberosPrincipal(fullName); } + if (cAPrinc != null) { + clientAlias = new KerberosPrincipal(cAPrinc.getName()); + } + // XXX Compare name to credName if (sPrinc != null) { @@ -205,11 +235,17 @@ public class Krb5InitCredential KerberosPrincipal.KRB_NT_SRV_INST); } + if (sAPrinc != null) { + serverAlias = new KerberosPrincipal(sAPrinc.getName()); + } + return new Krb5InitCredential(credName, delegatedCred, delegatedCred.getEncoded(), client, + clientAlias, server, + serverAlias, sessionKey.getBytes(), sessionKey.getEType(), delegatedCred.getFlags(), diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java index 8f79df1e6..387edd766 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java @@ -228,7 +228,7 @@ public class Krb5Util { public static KerberosTicket credsToTicket(Credentials serviceCreds) { EncryptionKey sessionKey = serviceCreds.getSessionKey(); - return new KerberosTicket( + KerberosTicket kt = new KerberosTicket( serviceCreds.getEncoded(), new KerberosPrincipal(serviceCreds.getClient().getName()), new KerberosPrincipal(serviceCreds.getServer().getName(), @@ -241,14 +241,35 @@ public class Krb5Util { serviceCreds.getEndTime(), serviceCreds.getRenewTill(), serviceCreds.getClientAddresses()); + PrincipalName clientAlias = serviceCreds.getClientAlias(); + PrincipalName serverAlias = serviceCreds.getServerAlias(); + if (clientAlias != null) { + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetClientAlias(kt, new KerberosPrincipal( + clientAlias.getName(), clientAlias.getNameType())); + } + if (serverAlias != null) { + KerberosSecrets.getJavaxSecurityAuthKerberosAccess() + .kerberosTicketSetServerAlias(kt, new KerberosPrincipal( + serverAlias.getName(), serverAlias.getNameType())); + } + return kt; }; public static Credentials ticketToCreds(KerberosTicket kerbTicket) throws KrbException, IOException { + KerberosPrincipal clientAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetClientAlias(kerbTicket); + KerberosPrincipal serverAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetServerAlias(kerbTicket); return new Credentials( kerbTicket.getEncoded(), kerbTicket.getClient().getName(), + (clientAlias != null ? clientAlias.getName() : null), kerbTicket.getServer().getName(), + (serverAlias != null ? serverAlias.getName() : null), kerbTicket.getSessionKey().getEncoded(), kerbTicket.getSessionKeyType(), kerbTicket.getFlags(), diff --git a/src/share/classes/sun/security/jgss/krb5/SubjectComber.java b/src/share/classes/sun/security/jgss/krb5/SubjectComber.java index a7100f07c..1bc1bf7d6 100644 --- a/src/share/classes/sun/security/jgss/krb5/SubjectComber.java +++ b/src/share/classes/sun/security/jgss/krb5/SubjectComber.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2002, 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.jgss.krb5; +import sun.security.krb5.KerberosSecrets; + import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.kerberos.KerberosKey; import javax.security.auth.Subject; @@ -182,24 +184,45 @@ class SubjectComber { } } else { + KerberosPrincipal serverAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetServerAlias(ticket); if (serverPrincipal == null || - ticket.getServer().getName().equals(serverPrincipal)) { - + ticket.getServer().getName().equals(serverPrincipal) || + (serverAlias != null && + serverPrincipal.equals( + serverAlias.getName()))) { + KerberosPrincipal clientAlias = KerberosSecrets + .getJavaxSecurityAuthKerberosAccess() + .kerberosTicketGetClientAlias(ticket); if (clientPrincipal == null || clientPrincipal.equals( - ticket.getClient().getName())) { + ticket.getClient().getName()) || + (clientAlias != null && + clientPrincipal.equals( + clientAlias.getName()))) { if (oneOnly) { return ticket; } else { // Record names so that tickets will // all belong to same principals if (clientPrincipal == null) { - clientPrincipal = - ticket.getClient().getName(); + if (clientAlias == null) { + clientPrincipal = + ticket.getClient().getName(); + } else { + clientPrincipal = + clientAlias.getName(); + } } if (serverPrincipal == null) { - serverPrincipal = - ticket.getServer().getName(); + if (serverAlias == null) { + serverPrincipal = + ticket.getServer().getName(); + } else { + serverPrincipal = + serverAlias.getName(); + } } answer.add(credClass.cast(ticket)); } diff --git a/src/share/classes/sun/security/krb5/Credentials.java b/src/share/classes/sun/security/krb5/Credentials.java index 195b86663..b364a628e 100644 --- a/src/share/classes/sun/security/krb5/Credentials.java +++ b/src/share/classes/sun/security/krb5/Credentials.java @@ -48,7 +48,9 @@ public class Credentials { Ticket ticket; PrincipalName client; + PrincipalName clientAlias; PrincipalName server; + PrincipalName serverAlias; EncryptionKey key; TicketFlags flags; KerberosTime authTime; @@ -78,7 +80,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, @@ -87,14 +91,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, @@ -104,7 +111,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; @@ -116,7 +125,9 @@ public class Credentials { public Credentials(byte[] encoding, String client, + String clientAlias, String server, + String serverAlias, byte[] keyBytes, int keyType, boolean[] flags, @@ -127,7 +138,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)), @@ -152,10 +167,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; } @@ -271,6 +294,7 @@ public class Credentials { return new KrbTgsReq(options, this, server, + serverAlias, null, // from null, // till null, // rtime @@ -488,7 +512,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()); @@ -516,7 +544,11 @@ public class Credentials { public String toString() { StringBuffer buffer = new StringBuffer("Credentials:"); buffer.append( "\n client=").append(client); + if (clientAlias != null) + buffer.append( "\n clientAlias=").append(clientAlias); buffer.append( "\n server=").append(server); + if (serverAlias != null) + buffer.append( "\n serverAlias=").append(serverAlias); if (authTime != null) { buffer.append("\n authTime=").append(authTime); } diff --git a/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java b/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java index 84ca79bc9..7d1ebd484 100644 --- a/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java +++ b/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java @@ -25,6 +25,7 @@ package sun.security.krb5; +import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.kerberos.KeyTab; @@ -39,6 +40,14 @@ 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); + /** * Returns the proxy for a KerberosTicket. */ diff --git a/src/share/classes/sun/security/krb5/KrbApReq.java b/src/share/classes/sun/security/krb5/KrbApReq.java index fbd05574f..e36aeac4d 100644 --- a/src/share/classes/sun/security/krb5/KrbApReq.java +++ b/src/share/classes/sun/security/krb5/KrbApReq.java @@ -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 @@ -361,7 +361,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, diff --git a/src/share/classes/sun/security/krb5/KrbAsRep.java b/src/share/classes/sun/security/krb5/KrbAsRep.java index d647b82ab..202e86c2b 100644 --- a/src/share/classes/sun/security/krb5/KrbAsRep.java +++ b/src/share/classes/sun/security/krb5/KrbAsRep.java @@ -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, diff --git a/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java index 4e16108e5..445fa19df 100644 --- a/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java +++ b/src/share/classes/sun/security/krb5/KrbAsReqBuilder.java @@ -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; diff --git a/src/share/classes/sun/security/krb5/KrbCred.java b/src/share/classes/sun/security/krb5/KrbCred.java index e6a478581..8dfe36c49 100644 --- a/src/share/classes/sun/security/krb5/KrbCred.java +++ b/src/share/classes/sun/security/krb5/KrbCred.java @@ -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 @@ -82,7 +82,9 @@ public class KrbCred { sAddrs= new HostAddresses(server); KrbTgsReq tgsReq = new KrbTgsReq(options, tgt, tgService, - null, null, null, null, sAddrs, null, null, null); + null, null, null, null, null, + sAddrs, // Only non-null for KRB_NT_SRV_HST, see JDK-8132111 + null, null, null); credMessg = createMessage(tgsReq.sendAndGetCreds(), key); obuf = credMessg.asn1Encode(); @@ -157,7 +159,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); } diff --git a/src/share/classes/sun/security/krb5/KrbTgsRep.java b/src/share/classes/sun/security/krb5/KrbTgsRep.java index d1f469d37..83f0b28c0 100644 --- a/src/share/classes/sun/security/krb5/KrbTgsRep.java +++ b/src/share/classes/sun/security/krb5/KrbTgsRep.java @@ -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; + } } diff --git a/src/share/classes/sun/security/krb5/KrbTgsReq.java b/src/share/classes/sun/security/krb5/KrbTgsReq.java index 6207a26ff..d8621eb5a 100644 --- a/src/share/classes/sun/security/krb5/KrbTgsReq.java +++ b/src/share/classes/sun/security/krb5/KrbTgsReq.java @@ -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 @@ -363,6 +373,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); } diff --git a/src/share/classes/sun/security/krb5/PrincipalName.java b/src/share/classes/sun/security/krb5/PrincipalName.java index b8e855a6a..4a16eb39c 100644 --- a/src/share/classes/sun/security/krb5/PrincipalName.java +++ b/src/share/classes/sun/security/krb5/PrincipalName.java @@ -553,7 +553,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()); diff --git a/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java b/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java index 25e29387a..6cfcd41d8 100644 --- a/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java +++ b/src/share/classes/sun/security/krb5/internal/CredentialsUtil.java @@ -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 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) { diff --git a/src/share/classes/sun/security/krb5/internal/KRBError.java b/src/share/classes/sun/security/krb5/internal/KRBError.java index 34247ab46..5b51706d3 100644 --- a/src/share/classes/sun/security/krb5/internal/KRBError.java +++ b/src/share/classes/sun/security/krb5/internal/KRBError.java @@ -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; diff --git a/src/share/classes/sun/security/krb5/internal/ReferralsCache.java b/src/share/classes/sun/security/krb5/internal/ReferralsCache.java index d21181600..970d432ab 100644 --- a/src/share/classes/sun/security/krb5/internal/ReferralsCache.java +++ b/src/share/classes/sun/security/krb5/internal/ReferralsCache.java @@ -45,8 +45,27 @@ import sun.security.krb5.PrincipalName; */ final class ReferralsCache { - private static Map> referralsMap = - new HashMap<>(); + private static Map> + 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 entries = referralsMap.get(service); + Map entries = referralsMap.get(k); if (entries == null) { entries = new HashMap(); - 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 entries = referralsMap.get(service); + static synchronized ReferralCacheEntry get(PrincipalName cname, + PrincipalName service, String fromRealm) { + ReferralCacheKey k = new ReferralCacheKey(cname, service); + pruneExpired(k); + Map 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 entries = referralsMap.get(service); + Map entries = referralsMap.get(k); if (entries != null) { for (Entry mapEntry : entries.entrySet()) { diff --git a/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java b/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java index c62e6293f..ccb9b1c0a 100644 --- a/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java +++ b/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java @@ -192,8 +192,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() { diff --git a/src/share/native/sun/security/krb5/nativeccache.c b/src/share/native/sun/security/krb5/nativeccache.c index 1b50a2e17..5ab3f737f 100644 --- a/src/share/native/sun/security/krb5/nativeccache.c +++ b/src/share/native/sun/security/krb5/nativeccache.c @@ -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 @@ -418,7 +418,7 @@ JNIEXPORT jobject JNICALL Java_sun_security_krb5_Credentials_acquireDefaultNativ if (krbcredsConstructor == 0) { krbcredsConstructor = (*env)->GetMethodID(env, krbcredsClass, "", - "(Lsun/security/krb5/internal/Ticket;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/EncryptionKey;Lsun/security/krb5/internal/TicketFlags;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/HostAddresses;)V"); + "(Lsun/security/krb5/internal/Ticket;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/PrincipalName;Lsun/security/krb5/EncryptionKey;Lsun/security/krb5/internal/TicketFlags;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/KerberosTime;Lsun/security/krb5/internal/HostAddresses;)V"); if (krbcredsConstructor == 0) { printf("Couldn't find sun.security.krb5.internal.Ticket constructor\n"); break; @@ -432,7 +432,9 @@ JNIEXPORT jobject JNICALL Java_sun_security_krb5_Credentials_acquireDefaultNativ krbcredsConstructor, ticket, clientPrincipal, + NULL, targetPrincipal, + NULL, encryptionKey, ticketFlags, authTime, diff --git a/src/windows/native/sun/security/krb5/NativeCreds.c b/src/windows/native/sun/security/krb5/NativeCreds.c index b7f81d2f0..ec891a929 100644 --- a/src/windows/native/sun/security/krb5/NativeCreds.c +++ b/src/windows/native/sun/security/krb5/NativeCreds.c @@ -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 @@ -404,6 +404,8 @@ JNIEXPORT jobject JNICALL Java_sun_security_krb5_Credentials_acquireDefaultNativ "(Lsun/security/krb5/internal/Ticket;" "Lsun/security/krb5/PrincipalName;" "Lsun/security/krb5/PrincipalName;" + "Lsun/security/krb5/PrincipalName;" + "Lsun/security/krb5/PrincipalName;" "Lsun/security/krb5/EncryptionKey;" "Lsun/security/krb5/internal/TicketFlags;" "Lsun/security/krb5/internal/KerberosTime;" @@ -665,7 +667,9 @@ JNIEXPORT jobject JNICALL Java_sun_security_krb5_Credentials_acquireDefaultNativ krbcredsConstructor, ticket, clientPrincipal, + NULL, targetPrincipal, + NULL, encryptionKey, ticketFlags, authTime, // mdu diff --git a/test/sun/security/krb5/auto/KDC.java b/test/sun/security/krb5/auto/KDC.java index 247d41845..9e9cd977b 100644 --- a/test/sun/security/krb5/auto/KDC.java +++ b/test/sun/security/krb5/auto/KDC.java @@ -804,8 +804,10 @@ public class KDC { PrincipalName cname = null; boolean allowForwardable = true; - + boolean isReferral = false; if (body.kdcOptions.get(KDCOptions.CANONICALIZE)) { + System.out.println(realm + "> verifying referral for " + + body.sname.getNameString()); KDC referral = aliasReferrals.get(body.sname.getNameString()); if (referral != null) { service = new PrincipalName( @@ -813,6 +815,9 @@ public class KDC { PrincipalName.NAME_COMPONENT_SEPARATOR_STR + referral.getRealm(), PrincipalName.KRB_NT_SRV_INST, this.getRealm()); + System.out.println(realm + "> referral to " + + referral.getRealm()); + isReferral = true; } } @@ -912,7 +917,8 @@ public class KDC { if (body.kdcOptions.get(KDCOptions.ALLOW_POSTDATE)) { bFlags[Krb5.TKT_OPTS_MAY_POSTDATE] = true; } - if (body.kdcOptions.get(KDCOptions.CNAME_IN_ADDL_TKT)) { + if (body.kdcOptions.get(KDCOptions.CNAME_IN_ADDL_TKT) && + !isReferral) { if (!options.containsKey(Option.ALLOW_S4U2PROXY)) { // Don't understand CNAME_IN_ADDL_TKT throw new KrbException(Krb5.KDC_ERR_BADOPTION); @@ -1073,8 +1079,7 @@ public class KDC { } int eType = eTypes[0]; - if (body.kdcOptions.get(KDCOptions.CANONICALIZE) && - body.cname.getNameType() == PrincipalName.KRB_NT_ENTERPRISE) { + if (body.kdcOptions.get(KDCOptions.CANONICALIZE)) { PrincipalName principal = alias2Principals.get( body.cname.getNameString()); if (principal != null) { diff --git a/test/sun/security/krb5/auto/ReferralsTest.java b/test/sun/security/krb5/auto/ReferralsTest.java index 9fc87b7e0..32e56344f 100644 --- a/test/sun/security/krb5/auto/ReferralsTest.java +++ b/test/sun/security/krb5/auto/ReferralsTest.java @@ -29,9 +29,18 @@ */ import java.io.File; -import sun.security.krb5.Credentials; -import sun.security.krb5.internal.CredentialsUtil; -import sun.security.krb5.KrbAsReqBuilder; +import java.security.Principal; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.Subject; + +import org.ietf.jgss.GSSName; + +import sun.security.jgss.GSSUtil; import sun.security.krb5.PrincipalName; public class ReferralsTest { @@ -40,39 +49,32 @@ public class ReferralsTest { private static final String realmKDC1 = "RABBIT.HOLE"; private static final String realmKDC2 = "DEV.RABBIT.HOLE"; private static final char[] password = "123qwe@Z".toCharArray(); + + // Names private static final String clientName = "test"; + private static final String serviceName = "http" + + PrincipalName.NAME_COMPONENT_SEPARATOR_STR + + "server.dev.rabbit.hole"; + // Alias private static final String clientAlias = clientName + PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; - private static final String clientKDC1QueryName = clientAlias.replaceAll( + // Names + realms + private static final String clientKDC1Name = clientAlias.replaceAll( PrincipalName.NAME_REALM_SEPARATOR_STR, "\\\\" + PrincipalName.NAME_REALM_SEPARATOR_STR) + PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1; - private static PrincipalName clientKDC1QueryPrincipal = null; - static { - try { - clientKDC1QueryPrincipal = new PrincipalName( - clientKDC1QueryName, PrincipalName.KRB_NT_ENTERPRISE, - null); - } catch (Throwable t) {} - } - private static final String clientKDC2Name = clientName + PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2; - - private static final String serviceName = "http" + - PrincipalName.NAME_COMPONENT_SEPARATOR_STR + - "server.dev.rabbit.hole"; - - private static Credentials tgt; - private static Credentials tgs; + private static final String serviceKDC2Name = serviceName + + PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2; public static void main(String[] args) throws Exception { try { initializeKDCs(); - getTGT(); - getTGS(); + testSubjectCredentials(); + testDelegated(); } finally { cleanup(); } @@ -107,6 +109,11 @@ public class ReferralsTest { kdc1.registerAlias(serviceName, kdc2); kdc2.registerAlias(clientAlias, clientKDC2Name); + Map> mapKDC2 = new HashMap<>(); + mapKDC2.put(serviceName + "@" + realmKDC2, Arrays.asList( + new String[]{serviceName + "@" + realmKDC2})); + kdc2.setOption(KDC.Option.ALLOW_S4U2PROXY, mapKDC2); + KDC.saveConfig(krbConfigName, kdc1, kdc2, "forwardable=true"); System.setProperty("java.security.krb5.conf", krbConfigName); @@ -119,50 +126,123 @@ public class ReferralsTest { } } - private static void getTGT() throws Exception { - KrbAsReqBuilder builder = new KrbAsReqBuilder(clientKDC1QueryPrincipal, - password); - tgt = builder.action().getCreds(); - builder.destroy(); + /* + * The client subject (whose principal is + * test@RABBIT.HOLE@RABBIT.HOLE) will obtain a TGT after + * realm referral and name canonicalization (TGT cname + * will be test@DEV.RABBIT.HOLE). With this TGT, the client will request + * a TGS for service http/server.dev.rabbit.hole@RABBIT.HOLE. After + * realm referral, a http/server.dev.rabbit.hole@DEV.RABBIT.HOLE TGS + * will be obtained. + * + * Assert that we get the proper TGT and TGS tickets, and that they are + * associated to the client subject. + * + * Assert that if we request a TGS for the same service again (based on the + * original service name), we don't get a new one but the previous, + * already in the subject credentials. + */ + private static void testSubjectCredentials() throws Exception { + Subject clientSubject = new Subject(); + Context clientContext = Context.fromUserPass(clientSubject, + clientKDC1Name, password, false); + + Set clientPrincipals = clientSubject.getPrincipals(); + if (clientPrincipals.size() != 1) { + throw new Exception("Only one client subject principal expected"); + } + Principal clientPrincipal = clientPrincipals.iterator().next(); if (DEBUG) { - System.out.println("TGT"); - System.out.println("----------------------"); - System.out.println(tgt); - System.out.println("----------------------"); + System.out.println("Client subject principal: " + + clientPrincipal.getName()); } - if (tgt == null) { - throw new Exception("TGT is null"); + if (!clientPrincipal.getName().equals(clientKDC1Name)) { + throw new Exception("Unexpected client subject principal."); } - if (!tgt.getClient().getName().equals(clientKDC2Name)) { - throw new Exception("Unexpected TGT client"); + + clientContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); + clientContext.take(new byte[0]); + Set clientTickets = + clientSubject.getPrivateCredentials(KerberosTicket.class); + boolean tgtFound = false; + boolean tgsFound = false; + for (KerberosTicket clientTicket : clientTickets) { + String cname = clientTicket.getClient().getName(); + String sname = clientTicket.getServer().getName(); + if (cname.equals(clientKDC2Name)) { + if (sname.equals(PrincipalName.TGS_DEFAULT_SRV_NAME + + PrincipalName.NAME_COMPONENT_SEPARATOR_STR + + realmKDC2 + PrincipalName.NAME_REALM_SEPARATOR_STR + + realmKDC2)) { + tgtFound = true; + } else if (sname.equals(serviceKDC2Name)) { + tgsFound = true; + } + } + if (DEBUG) { + System.out.println("Client subject KerberosTicket:"); + System.out.println(clientTicket); + } } - String[] tgtServerNames = tgt.getServer().getNameStrings(); - if (tgtServerNames.length != 2 || !tgtServerNames[0].equals( - PrincipalName.TGS_DEFAULT_SRV_NAME) || - !tgtServerNames[1].equals(realmKDC2) || - !tgt.getServer().getRealmString().equals(realmKDC2)) { - throw new Exception("Unexpected TGT server"); + if (!tgtFound || !tgsFound) { + throw new Exception("client subject tickets (TGT/TGS) not found."); } - } - - private static void getTGS() throws Exception { - tgs = CredentialsUtil.acquireServiceCreds(serviceName + - PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1, tgt); + int numOfTickets = clientTickets.size(); + clientContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); + clientContext.take(new byte[0]); + clientContext.status(); + int newNumOfTickets = + clientSubject.getPrivateCredentials(KerberosTicket.class).size(); if (DEBUG) { - System.out.println("TGS"); - System.out.println("----------------------"); - System.out.println(tgs); - System.out.println("----------------------"); + System.out.println("client subject number of tickets: " + + numOfTickets); + System.out.println("client subject new number of tickets: " + + newNumOfTickets); + } + if (numOfTickets != newNumOfTickets) { + throw new Exception("Useless client subject TGS request because" + + " TGS was not found in private credentials."); } - if (tgs == null) { - throw new Exception("TGS is null"); + } + + /* + * The server (http/server.dev.rabbit.hole@DEV.RABBIT.HOLE) + * will authenticate on itself on behalf of the client + * (test@DEV.RABBIT.HOLE). Cross-realm referrals will occur + * when requesting different TGTs and TGSs (including the + * request for delegated credentials). + */ + private static void testDelegated() throws Exception { + Context c = Context.fromUserPass(clientKDC2Name, + password, false); + c.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); + Context s = Context.fromUserPass(serviceKDC2Name, + password, true); + s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + Context.handshake(c, s); + Context delegatedContext = s.delegated(); + delegatedContext.startAsClient(serviceName, GSSUtil.GSS_KRB5_MECH_OID); + delegatedContext.x().requestMutualAuth(false); + Context s2 = Context.fromUserPass(serviceKDC2Name, + password, true); + s2.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + + // Test authentication + Context.handshake(delegatedContext, s2); + if (!delegatedContext.x().isEstablished() || !s2.x().isEstablished()) { + throw new Exception("Delegated authentication failed"); } - if (!tgs.getClient().getName().equals(clientKDC2Name)) { - throw new Exception("Unexpected TGS client"); + + // Test identities + GSSName contextInitiatorName = delegatedContext.x().getSrcName(); + GSSName contextAcceptorName = delegatedContext.x().getTargName(); + if (DEBUG) { + System.out.println("Context initiator: " + contextInitiatorName); + System.out.println("Context acceptor: " + contextAcceptorName); } - if (!tgs.getServer().getNameString().equals(serviceName) || - !tgs.getServer().getRealmString().equals(realmKDC2)) { - throw new Exception("Unexpected TGS server"); + if (!contextInitiatorName.toString().equals(clientKDC2Name) || + !contextAcceptorName.toString().equals(serviceName)) { + throw new Exception("Unexpected initiator or acceptor names"); } } } -- GitLab