8227437: S4U2proxy cannot continue because server's TGT cannot be found
authormbalao
Wed, 17 Jul 2019 12:26:56 -0300
changeset 57487 643978a35f6e
parent 57486 347804d623fc
child 57488 94691d8e746f
8227437: S4U2proxy cannot continue because server's TGT cannot be found Reviewed-by: weijun
src/java.security.jgss/macosx/native/libosxkrb5/nativeccache.c
src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java
src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java
src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java
src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java
src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java
src/java.security.jgss/share/classes/sun/security/jgss/krb5/SubjectComber.java
src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java
src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java
src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java
src/java.security.jgss/share/classes/sun/security/krb5/KrbAsRep.java
src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java
src/java.security.jgss/share/classes/sun/security/krb5/KrbCred.java
src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsRep.java
src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java
src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java
src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java
src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java
src/java.security.jgss/share/classes/sun/security/krb5/internal/ReferralsCache.java
src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/Credentials.java
src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c
test/jdk/sun/security/krb5/auto/KDC.java
test/jdk/sun/security/krb5/auto/ReferralsTest.java
--- a/src/java.security.jgss/macosx/native/libosxkrb5/nativeccache.c	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/macosx/native/libosxkrb5/nativeccache.c	Wed Jul 17 12:26:56 2019 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2015, 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
@@ -345,7 +345,7 @@
 
                     if (krbcredsConstructor == 0) {
                         krbcredsConstructor = (*env)->GetMethodID(env, krbcredsClass, "<init>",
-                                                                  "(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;
@@ -359,7 +359,9 @@
                                                  krbcredsConstructor,
                                                  ticket,
                                                  clientPrincipal,
+                                                 NULL,
                                                  targetPrincipal,
+                                                 NULL,
                                                  encryptionKey,
                                                  ticketFlags,
                                                  authTime,
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java	Wed Jul 17 12:26:56 2019 -0300
@@ -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
@@ -26,8 +26,6 @@
 package javax.security.auth.kerberos;
 
 import sun.security.krb5.JavaxSecurityAuthKerberosAccess;
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.PrincipalName;
 
 class JavaxSecurityAuthKerberosAccessImpl
         implements JavaxSecurityAuthKerberosAccess {
@@ -35,4 +33,20 @@
             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;
+    }
 }
--- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java	Wed Jul 17 12:26:56 2019 -0300
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2017, 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
@@ -195,6 +195,10 @@
 
     private transient boolean destroyed = false;
 
+    transient KerberosPrincipal clientAlias = null;
+
+    transient KerberosPrincipal serverAlias = null;
+
     /**
      * Constructs a {@code KerberosTicket} using credentials information that a
      * client either receives from a KDC or reads from a cache.
@@ -591,7 +595,11 @@
         try {
             krb5Creds = new sun.security.krb5.Credentials(asn1Encoding,
                                                     client.getName(),
+                                                    (clientAlias != null ?
+                                                            clientAlias.getName() : null),
                                                     server.getName(),
+                                                    (serverAlias != null ?
+                                                            serverAlias.getName() : null),
                                                     sessionKey.getEncoded(),
                                                     sessionKey.getKeyType(),
                                                     flags,
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java	Wed Jul 17 12:26:56 2019 -0300
@@ -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
@@ -713,14 +713,14 @@
                             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<Void>() {
                                       public Void run() {
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java	Wed Jul 17 12:26:56 2019 -0300
@@ -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
@@ -59,7 +59,9 @@
     private Krb5InitCredential(Krb5NameElement name,
                                byte[] asn1Encoding,
                                KerberosPrincipal client,
+                               KerberosPrincipal clientAlias,
                                KerberosPrincipal server,
+                               KerberosPrincipal serverAlias,
                                byte[] sessionKey,
                                int keyType,
                                boolean[] flags,
@@ -80,14 +82,21 @@
               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,
@@ -110,7 +119,9 @@
                                Credentials delegatedCred,
                                byte[] asn1Encoding,
                                KerberosPrincipal client,
+                               KerberosPrincipal clientAlias,
                                KerberosPrincipal server,
+                               KerberosPrincipal serverAlias,
                                byte[] sessionKey,
                                int keyType,
                                boolean[] flags,
@@ -131,7 +142,10 @@
               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.
@@ -153,10 +167,18 @@
                                        Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
         }
 
+        KerberosPrincipal clientAlias = KerberosSecrets
+                .getJavaxSecurityAuthKerberosAccess()
+                .kerberosTicketGetClientAlias(tgt);
+        KerberosPrincipal serverAlias = KerberosSecrets
+                .getJavaxSecurityAuthKerberosAccess()
+                .kerberosTicketGetServerAlias(tgt);
         return new Krb5InitCredential(name,
                                       tgt.getEncoded(),
                                       tgt.getClient(),
+                                      clientAlias,
                                       tgt.getServer(),
+                                      serverAlias,
                                       tgt.getSessionKey().getEncoded(),
                                       tgt.getSessionKeyType(),
                                       tgt.getFlags(),
@@ -179,10 +201,14 @@
          */
 
         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;
 
@@ -193,6 +219,10 @@
             client =  new KerberosPrincipal(fullName);
         }
 
+        if (cAPrinc != null) {
+            clientAlias = new KerberosPrincipal(cAPrinc.getName());
+        }
+
         // XXX Compare name to credName
 
         if (sPrinc != null) {
@@ -201,11 +231,17 @@
                                         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(),
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java	Wed Jul 17 12:26:56 2019 -0300
@@ -132,7 +132,7 @@
 
     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(),
@@ -145,14 +145,35 @@
             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(),
--- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/SubjectComber.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/SubjectComber.java	Wed Jul 17 12:26:56 2019 -0300
@@ -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 @@
 
                                 }
                             } 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));
                                         }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java	Wed Jul 17 12:26:56 2019 -0300
@@ -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 @@
 
     Ticket ticket;
     PrincipalName client;
+    PrincipalName clientAlias;
     PrincipalName server;
+    PrincipalName serverAlias;
     EncryptionKey key;
     TicketFlags flags;
     KerberosTime authTime;
@@ -69,7 +71,9 @@
 
     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 @@
                        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 @@
                        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 Credentials(byte[] encoding,
                        String client,
+                       String clientAlias,
                        String server,
+                       String serverAlias,
                        byte[] keyBytes,
                        int keyType,
                        boolean[] flags,
@@ -118,7 +129,11 @@
                        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 @@
         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 @@
         return new KrbTgsReq(options,
                              this,
                              server,
+                             serverAlias,
                              null, // from
                              null, // till
                              null, // rtime
@@ -484,7 +508,11 @@
     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 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);
         }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java	Wed Jul 17 12:26:56 2019 -0300
@@ -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 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);
 }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java	Wed Jul 17 12:26:56 2019 -0300
@@ -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 @@
         creds = new Credentials(
                                 apReqMessg.ticket,
                                 authenticator.cname,
+                                null,
                                 apReqMessg.ticket.sname,
+                                null,
                                 enc_ticketPart.key,
                                 enc_ticketPart.flags,
                                 enc_ticketPart.authtime,
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsRep.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsRep.java	Wed Jul 17 12:26:56 2019 -0300
@@ -118,7 +118,7 @@
                     "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 @@
                 password,
                 encPartKeyType,
                 PAData.getSaltAndParams(encPartKeyType, rep.pAData));
-        decrypt(dkey, asReq);
+        decrypt(dkey, asReq, cname);
     }
 
     /**
@@ -144,7 +144,8 @@
      * @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 @@
         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,
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbAsReqBuilder.java	Wed Jul 17 12:26:56 2019 -0300
@@ -68,6 +68,7 @@
     // 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 @@
     private void init(PrincipalName cname)
             throws KrbException {
         this.cname = cname;
+        this.refCname = cname;
         state = State.INIT;
     }
 
@@ -284,7 +286,7 @@
         }
         return new KrbAsReq(key,
             options,
-            cname,
+            refCname,
             sname,
             from,
             till,
@@ -334,7 +336,7 @@
         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 @@
 
         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 @@
                     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;
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbCred.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbCred.java	Wed Jul 17 12:26:56 2019 -0300
@@ -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 @@
         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 @@
                                + " 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);
     }
 
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsRep.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsRep.java	Wed Jul 17 12:26:56 2019 -0300
@@ -86,9 +86,20 @@
 
         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 @@
     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;
+    }
 }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/KrbTgsReq.java	Wed Jul 17 12:26:56 2019 -0300
@@ -45,7 +45,9 @@
 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 @@
 
     // 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 @@
             KDCOptions options,
             Credentials asCreds,
             PrincipalName sname,
+            PrincipalName serverAlias,
             KerberosTime from,
             KerberosTime till,
             KerberosTime rtime,
@@ -90,16 +96,18 @@
             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 @@
             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 @@
         return secondTicket;
     }
 
+    PrincipalName getClientAlias() {
+        return clientAlias;
+    }
+
+    PrincipalName getServerAlias() {
+        return serverAlias;
+    }
+
     private static void debug(String message) {
         //      System.err.println(">>> KrbTgsReq: " + message);
     }
--- a/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/PrincipalName.java	Wed Jul 17 12:26:56 2019 -0300
@@ -564,7 +564,9 @@
         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());
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/CredentialsUtil.java	Wed Jul 17 12:26:56 2019 -0300
@@ -284,8 +284,9 @@
                 // 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 @@
         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 @@
                     // 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 @@
      */
     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 @@
             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) {
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/KRBError.java	Wed Jul 17 12:26:56 2019 -0300
@@ -139,7 +139,7 @@
         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 @@
         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;
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ReferralsCache.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/ReferralsCache.java	Wed Jul 17 12:26:56 2019 -0300
@@ -45,8 +45,27 @@
  */
 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 @@
     }
 
     /*
-     * 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 @@
      * 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 @@
     }
 
     /*
-     * 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 @@
     /*
      * 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()) {
--- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/Credentials.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/Credentials.java	Wed Jul 17 12:26:56 2019 -0300
@@ -180,8 +180,9 @@
         // 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() {
--- a/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c	Wed Jul 17 16:13:26 2019 -0700
+++ b/src/java.security.jgss/windows/native/libw2k_lsa_auth/NativeCreds.c	Wed Jul 17 12:26:56 2019 -0300
@@ -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
@@ -406,6 +406,8 @@
                     "(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;"
@@ -667,7 +669,9 @@
                 krbcredsConstructor,
                 ticket,
                 clientPrincipal,
+                NULL,
                 targetPrincipal,
+                NULL,
                 encryptionKey,
                 ticketFlags,
                 authTime, // mdu
--- a/test/jdk/sun/security/krb5/auto/KDC.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/test/jdk/sun/security/krb5/auto/KDC.java	Wed Jul 17 12:26:56 2019 -0300
@@ -808,8 +808,10 @@
 
             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(
@@ -817,6 +819,9 @@
                             PrincipalName.NAME_COMPONENT_SEPARATOR_STR +
                             referral.getRealm(), PrincipalName.KRB_NT_SRV_INST,
                             this.getRealm());
+                    System.out.println(realm + "> referral to " +
+                            referral.getRealm());
+                    isReferral = true;
                 }
             }
 
@@ -918,7 +923,8 @@
             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);
@@ -1074,8 +1080,7 @@
             }
             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) {
--- a/test/jdk/sun/security/krb5/auto/ReferralsTest.java	Wed Jul 17 16:13:26 2019 -0700
+++ b/test/jdk/sun/security/krb5/auto/ReferralsTest.java	Wed Jul 17 12:26:56 2019 -0300
@@ -30,9 +30,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 {
@@ -41,39 +50,32 @@
     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 clientAlias = clientName +
-            PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
-
-    private static final String clientKDC1QueryName = 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;
+    // Alias
+    private static final String clientAlias = clientName +
+            PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1;
+
+    // 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 final String clientKDC2Name = clientName +
+            PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC2;
+    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();
         }
@@ -108,6 +110,11 @@
         kdc1.registerAlias(serviceName, kdc2);
         kdc2.registerAlias(clientAlias, clientKDC2Name);
 
+        Map<String,List<String>> 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);
@@ -120,50 +127,123 @@
         }
     }
 
-    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<Principal> 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 (!clientPrincipal.getName().equals(clientKDC1Name)) {
+            throw new Exception("Unexpected client subject principal.");
         }
-        if (tgt == null) {
-            throw new Exception("TGT is null");
-        }
-        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<KerberosTicket> 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.");
+        }
+        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("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.");
         }
     }
 
-    private static void getTGS() throws Exception {
-        tgs = CredentialsUtil.acquireServiceCreds(serviceName +
-                PrincipalName.NAME_REALM_SEPARATOR_STR + realmKDC1, tgt);
-        if (DEBUG) {
-            System.out.println("TGS");
-            System.out.println("----------------------");
-            System.out.println(tgs);
-            System.out.println("----------------------");
+    /*
+     * 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 == null) {
-            throw new Exception("TGS is null");
+
+        // 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.getClient().getName().equals(clientKDC2Name)) {
-            throw new Exception("Unexpected TGS client");
-        }
-        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");
         }
     }
 }