changeset 13691:96cab194659e

8220302: Better Kerberos ccache handling Reviewed-by: valeriep
author weijun
date Fri, 19 Apr 2019 10:22:20 +0800
parents f0bbf5b47d57
children 8f88a036006e
files src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java src/share/classes/javax/security/auth/kerberos/KerberosTicket.java src/share/classes/sun/security/jgss/krb5/Krb5Context.java src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java src/share/classes/sun/security/jgss/krb5/Krb5NameElement.java src/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java src/share/classes/sun/security/jgss/krb5/Krb5Util.java src/share/classes/sun/security/krb5/Credentials.java src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java src/share/classes/sun/security/krb5/Realm.java src/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java src/share/classes/sun/security/krb5/internal/ccache/CCacheOutputStream.java src/share/classes/sun/security/krb5/internal/ccache/Credentials.java src/share/classes/sun/security/krb5/internal/ccache/CredentialsCache.java src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java src/share/classes/sun/security/util/SecurityProperties.java src/share/lib/security/java.security-aix src/share/lib/security/java.security-linux src/share/lib/security/java.security-macosx src/share/lib/security/java.security-solaris src/share/lib/security/java.security-windows src/windows/classes/sun/security/krb5/internal/tools/Klist.java
diffstat 24 files changed, 614 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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
@@ -668,7 +668,11 @@
                     // check to renew credentials
                     if (!isCurrent(cred)) {
                         if (renewTGT) {
-                            cred = renewCredentials(cred);
+                            Credentials newCred = renewCredentials(cred);
+                            if (newCred != null) {
+                                newCred.setProxy(cred.getProxy());
+                            }
+                            cred = newCred;
                         } else {
                             // credentials have expired
                             cred = null;
@@ -1074,6 +1078,10 @@
             // create Kerberos Ticket
             if (isInitiator) {
                 kerbTicket = Krb5Util.credsToTicket(cred);
+                if (cred.getProxy() != null) {
+                    KerberosSecrets.getJavaxSecurityAuthKerberosAccess()
+                            .kerberosTicketSetProxy(kerbTicket,Krb5Util.credsToTicket(cred.getProxy()));
+                }
             }
 
             if (storeKey && encKeys != null) {
--- a/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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,10 @@
             KeyTab ktab) {
         return ktab.takeSnapshot();
     }
+    public KerberosTicket kerberosTicketGetProxy(KerberosTicket t) {
+        return t.proxy;
+    }
+    public void kerberosTicketSetProxy(KerberosTicket t, KerberosTicket p) {
+        t.proxy = p;
+    }
 }
--- a/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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
@@ -29,11 +29,13 @@
 import java.util.Date;
 import java.util.Arrays;
 import java.net.InetAddress;
+import java.util.Objects;
 import javax.crypto.SecretKey;
 import javax.security.auth.Refreshable;
 import javax.security.auth.Destroyable;
 import javax.security.auth.RefreshFailedException;
 import javax.security.auth.DestroyFailedException;
+
 import sun.misc.HexDumpEncoder;
 import sun.security.krb5.EncryptionKey;
 import sun.security.krb5.Asn1Exception;
@@ -190,8 +192,13 @@
      * @serial
      */
 
+    private InetAddress[] clientAddresses;
 
-    private InetAddress[] clientAddresses;
+    /**
+     * Evidence ticket if proxy_impersonator. This field can be accessed
+     * by KerberosSecrets. It's serialized.
+     */
+    KerberosTicket proxy = null;
 
     private transient boolean destroyed = false;
 
@@ -662,6 +669,7 @@
                 "Renew Till = " + String.valueOf(renewTill) + "\n" +
                 "Client Addresses " +
                 (clientAddresses == null ? " Null " : caddrBuf.toString() +
+                (proxy == null ? "" : "\nwith a proxy ticket") +
                 "\n"));
     }
 
@@ -699,6 +707,10 @@
 
         // clientAddress may be null, the array's hashCode is 0
         result = result * 37 + Arrays.hashCode(clientAddresses);
+
+        if (proxy != null) {
+            result = result * 37 + proxy.hashCode();
+        }
         return result * 37 + Arrays.hashCode(flags);
     }
 
@@ -764,6 +776,10 @@
                 return false;
         }
 
+        if (!Objects.equals(proxy, otherTicket.proxy)) {
+            return false;
+        }
+
         return true;
     }
 
--- a/src/share/classes/sun/security/jgss/krb5/Krb5Context.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5Context.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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
@@ -614,6 +614,8 @@
                     if (myCred == null) {
                         myCred = Krb5InitCredential.getInstance(caller, myName,
                                               GSSCredential.DEFAULT_LIFETIME);
+                        myCred = Krb5ProxyCredential.tryImpersonation(
+                                caller, (Krb5InitCredential)myCred);
                     } else if (!myCred.isInitiatorCredential()) {
                         throw new GSSException(errorCode, -1,
                                            "No TGT available");
@@ -651,8 +653,8 @@
                                     // highly consider just calling:
                                     // Subject.getSubject
                                     // SubjectComber.find
-                                    // instead of Krb5Util.getTicket
-                                    return Krb5Util.getTicket(
+                                    // instead of Krb5Util.getServiceTicket
+                                    return Krb5Util.getServiceTicket(
                                         GSSCaller.CALLER_UNKNOWN,
                                         // since it's useSubjectCredsOnly here,
                                         // don't worry about the null
--- a/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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
@@ -55,6 +55,7 @@
 
     private Krb5NameElement name;
     private Credentials krb5Credentials;
+    public KerberosTicket proxyTicket;
 
     private Krb5InitCredential(Krb5NameElement name,
                                byte[] asn1Encoding,
@@ -153,7 +154,7 @@
                                        Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
         }
 
-        return new Krb5InitCredential(name,
+        Krb5InitCredential result = new Krb5InitCredential(name,
                                       tgt.getEncoded(),
                                       tgt.getClient(),
                                       tgt.getServer(),
@@ -165,6 +166,9 @@
                                       tgt.getEndTime(),
                                       tgt.getRenewTill(),
                                       tgt.getClientAddresses());
+        result.proxyTicket = KerberosSecrets.getJavaxSecurityAuthKerberosAccess().
+            kerberosTicketGetProxy(tgt);
+        return result;
     }
 
     static Krb5InitCredential getInstance(Krb5NameElement name,
@@ -334,9 +338,9 @@
                 public KerberosTicket run() throws Exception {
                     // It's OK to use null as serverPrincipal. TGT is almost
                     // the first ticket for a principal and we use list.
-                    return Krb5Util.getTicket(
+                    return Krb5Util.getInitialTicket(
                         realCaller,
-                        clientPrincipal, null, acc);
+                        clientPrincipal, acc);
                         }});
         } catch (PrivilegedActionException e) {
             GSSException ge =
--- a/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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
@@ -120,6 +120,8 @@
                 usage == GSSCredential.INITIATE_AND_ACCEPT) {
                 credElement = Krb5InitCredential.getInstance
                     (caller, (Krb5NameElement) name, initLifetime);
+                credElement = Krb5ProxyCredential.tryImpersonation(
+                        caller, (Krb5InitCredential)credElement);
                 checkInitCredPermission
                     ((Krb5NameElement) credElement.getName());
             } else if (usage == GSSCredential.ACCEPT_ONLY) {
--- a/src/share/classes/sun/security/jgss/krb5/Krb5NameElement.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5NameElement.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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
@@ -144,7 +144,7 @@
         return new Krb5NameElement(principalName, gssNameStr, gssNameType);
     }
 
-    static Krb5NameElement getInstance(PrincipalName principalName) {
+    public static Krb5NameElement getInstance(PrincipalName principalName) {
         return new Krb5NameElement(principalName,
                                    principalName.getName(),
                                    Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL);
--- a/src/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java	Fri Apr 19 10:22:20 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 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,10 +26,17 @@
 package sun.security.jgss.krb5;
 
 import org.ietf.jgss.*;
+import sun.security.jgss.GSSCaller;
 import sun.security.jgss.spi.*;
-import java.util.Date;
+
+import java.io.IOException;
+
+import sun.security.krb5.Credentials;
+import sun.security.krb5.KrbException;
 import sun.security.krb5.internal.Ticket;
 
+import javax.security.auth.kerberos.KerberosTicket;
+
 /**
  * Implements the krb5 proxy credential element used in constrained
  * delegation. It is used in both impersonation (where there is no Kerberos 5
@@ -112,4 +119,24 @@
         throw new GSSException(GSSException.FAILURE, -1,
                 "Only an initiate credentials can impersonate");
     }
+
+    // Try to see if a default credential should act as an impersonator.
+    static Krb5CredElement tryImpersonation(GSSCaller caller,
+            Krb5InitCredential initiator) throws GSSException {
+
+        try {
+            KerberosTicket proxy = initiator.proxyTicket;
+            if (proxy != null) {
+                Credentials proxyCreds = Krb5Util.ticketToCreds(proxy);
+                return new Krb5ProxyCredential(initiator,
+                        Krb5NameElement.getInstance(proxyCreds.getClient()),
+                        proxyCreds.getTicket());
+            } else {
+                return initiator;
+            }
+        } catch (KrbException | IOException e) {
+            throw new GSSException(GSSException.DEFECTIVE_CREDENTIAL, -1,
+                    "Cannot create proxy credential");
+        }
+    }
 }
--- a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java	Fri Apr 19 10:22:20 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -139,11 +139,8 @@
     /**
      * Retrieves the ticket corresponding to the client/server principal
      * pair from the Subject in the specified AccessControlContext.
-     * If the ticket can not be found in the Subject, and if
-     * useSubjectCredsOnly is false, then obtain ticket from
-     * a LoginContext.
      */
-    static KerberosTicket getTicket(GSSCaller caller,
+    static KerberosTicket getServiceTicket(GSSCaller caller,
         String clientPrincipal, String serverPrincipal,
         AccessControlContext acc) throws LoginException {
 
@@ -153,11 +150,31 @@
             SubjectComber.find(accSubj, serverPrincipal, clientPrincipal,
                   KerberosTicket.class);
 
+        return ticket;
+    }
+
+    /**
+     * Retrieves the initial TGT corresponding to the client principal
+     * from the Subject in the specified AccessControlContext.
+     * If the ticket can not be found in the Subject, and if
+     * useSubjectCredsOnly is false, then obtain ticket from
+     * a LoginContext.
+     */
+    static KerberosTicket getInitialTicket(GSSCaller caller,
+            String clientPrincipal,
+            AccessControlContext acc) throws LoginException {
+
+        // Try to get ticket from acc's Subject
+        Subject accSubj = Subject.getSubject(acc);
+        KerberosTicket ticket =
+                SubjectComber.find(accSubj, null, clientPrincipal,
+                        KerberosTicket.class);
+
         // Try to get ticket from Subject obtained from GSSUtil
         if (ticket == null && !GSSUtil.useSubjectCredsOnly(caller)) {
             Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID);
             ticket = SubjectComber.find(subject,
-                serverPrincipal, clientPrincipal, KerberosTicket.class);
+                    null, clientPrincipal, KerberosTicket.class);
         }
         return ticket;
     }
--- a/src/share/classes/sun/security/krb5/Credentials.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/krb5/Credentials.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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
@@ -56,13 +56,23 @@
     KerberosTime endTime;
     KerberosTime renewTill;
     HostAddresses cAddr;
-    EncryptionKey serviceKey;
     AuthorizationData authzData;
     private static boolean DEBUG = Krb5.DEBUG;
     private static CredentialsCache cache;
     static boolean alreadyLoaded = false;
     private static boolean alreadyTried = false;
 
+    private Credentials proxy = null;
+
+    public Credentials getProxy() {
+        return proxy;
+    }
+
+    public Credentials setProxy(Credentials proxy) {
+        this.proxy = proxy;
+        return this;
+    }
+
     // Read native ticket with session key type in the given list
     private static native Credentials acquireDefaultNativeCreds(int[] eTypes);
 
@@ -336,20 +346,19 @@
             return null;
         }
 
-        sun.security.krb5.internal.ccache.Credentials tgtCred  =
-            ccache.getDefaultCreds();
+        Credentials tgtCred = ccache.getInitialCreds();
 
         if (tgtCred == null) {
             return null;
         }
 
-        if (EType.isSupported(tgtCred.getEType())) {
-            return tgtCred.setKrbCreds();
+        if (EType.isSupported(tgtCred.key.getEType())) {
+            return tgtCred;
         } else {
             if (DEBUG) {
                 System.out.println(
                     ">>> unsupported key type found the default TGT: " +
-                    tgtCred.getEType());
+                    tgtCred.key.getEType());
             }
             return null;
         }
@@ -384,20 +393,19 @@
             cache = CredentialsCache.getInstance();
         }
         if (cache != null) {
-            sun.security.krb5.internal.ccache.Credentials temp =
-                cache.getDefaultCreds();
+            Credentials temp = cache.getInitialCreds();
             if (temp != null) {
                 if (DEBUG) {
                     System.out.println(">>> KrbCreds found the default ticket"
                             + " granting ticket in credential cache.");
                 }
-                if (EType.isSupported(temp.getEType())) {
-                    result = temp.setKrbCreds();
+                if (EType.isSupported(temp.key.getEType())) {
+                    result = temp;
                 } else {
                     if (DEBUG) {
                         System.out.println(
                             ">>> unsupported key type found the default TGT: " +
-                            temp.getEType());
+                            temp.key.getEType());
                     }
                 }
             }
@@ -474,10 +482,6 @@
         return cache;
     }
 
-    public EncryptionKey getServiceKey() {
-        return serviceKey;
-    }
-
     /*
      * Prints out debug info.
      */
--- a/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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,9 +25,8 @@
 
 package sun.security.krb5;
 
+import javax.security.auth.kerberos.KerberosTicket;
 import javax.security.auth.kerberos.KeyTab;
-import sun.security.krb5.EncryptionKey;
-import sun.security.krb5.PrincipalName;
 
 /**
  * An unsafe tunnel to get non-public access to classes in the
@@ -39,4 +38,14 @@
      */
     public sun.security.krb5.internal.ktab.KeyTab keyTabTakeSnapshot(
             KeyTab ktab);
+
+    /**
+     * Returns the proxy for a KerberosTicket.
+     */
+    public KerberosTicket kerberosTicketGetProxy(KerberosTicket t);
+
+    /**
+     * Sets the proxy for a KerberosTicket.
+     */
+    public void kerberosTicketSetProxy(KerberosTicket t, KerberosTicket p);
 }
--- a/src/share/classes/sun/security/krb5/Realm.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/krb5/Realm.java	Fri Apr 19 10:22:20 2019 +0800
@@ -188,7 +188,6 @@
             return false;
         for (int i = 0; i < name.length(); i++) {
             if (name.charAt(i) == '/' ||
-                name.charAt(i) == ':' ||
                 name.charAt(i) == '\0') {
                 return false;
             }
--- a/src/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java	Fri Apr 19 10:22:20 2019 +0800
@@ -325,16 +325,13 @@
     }
 
     /**
-     * Reads the next cred in stream.
-     * @return the next cred, null if ticket or second_ticket unparseable.
+     * Reads the next cred or config entry in stream.
+     * @return the next cred or config entry, null if data unparseable.
      *
-     * Note: MIT krb5 1.8.1 might generate a config entry with server principal
-     * X-CACHECONF:/krb5_ccache_conf_data/fast_avail/krbtgt/REALM@REALM. The
-     * entry is used by KDC to inform the client that it support certain
-     * features. Its ticket is not a valid krb5 ticket and thus this method
-     * returns null.
+     * When data is unparseable, this method makes sure the correct number of
+     * bytes are consumed so it's safe to start reading the next element.
      */
-    Credentials readCred(int version) throws IOException,RealmException, KrbApErrException, Asn1Exception {
+    Object readCred(int version) throws IOException,RealmException, KrbApErrException, Asn1Exception {
         PrincipalName cpname = null;
         try {
             cpname = readPrincipal(version);
@@ -396,12 +393,23 @@
         }
 
         try {
+            if (spname.getRealmString().equals("X-CACHECONF:")) {
+                String[] nameParts = spname.getNameStrings();
+                if (nameParts[0].equals("krb5_ccache_conf_data")) {
+                    return new CredentialsCache.ConfigEntry(nameParts[1],
+                            nameParts.length > 2 ? new PrincipalName(nameParts[2]) : null,
+                            ticketData);
+                }
+            }
             return new Credentials(cpname, spname, key, authtime, starttime,
-                endtime, renewTill, skey, tFlags,
-                addrs, auData,
-                ticketData != null ? new Ticket(ticketData) : null,
-                ticketData2 != null ? new Ticket(ticketData2) : null);
+                    endtime, renewTill, skey, tFlags,
+                    addrs, auData,
+                    ticketData != null ? new Ticket(ticketData) : null,
+                    ticketData2 != null ? new Ticket(ticketData2) : null);
         } catch (Exception e) {     // If any of new Ticket(*) fails.
+            if (DEBUG) {
+                e.printStackTrace(System.out);
+            }
             return null;
         }
     }
--- a/src/share/classes/sun/security/krb5/internal/ccache/CCacheOutputStream.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/krb5/internal/ccache/CCacheOutputStream.java	Fri Apr 19 10:22:20 2019 +0800
@@ -31,7 +31,6 @@
 package sun.security.krb5.internal.ccache;
 
 import java.io.IOException;
-import java.io.FileOutputStream;
 import java.io.OutputStream;
 import sun.security.krb5.internal.util.KrbDataOutputStream;
 import sun.security.krb5.*;
@@ -98,6 +97,21 @@
         writeTicket(creds.secondTicket);
     }
 
+    public void addConfigEntry(PrincipalName cname, CredentialsCache.ConfigEntry e)
+            throws IOException {
+        cname.writePrincipal(this);
+        e.getSName().writePrincipal(this);
+        write16(0); write16(0); write32(0);
+        write32(0); write32(0); write32(0); write32(0);
+        write8(0);
+        write32(0);
+        write32(0);
+        write32(0);
+        write32(e.getData().length);
+        write(e.getData());
+        write32(0);
+    }
+
     void writeTicket(Ticket t) throws IOException, Asn1Exception {
         if (t == null) {
             write32(0);
--- a/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java	Fri Apr 19 10:22:20 2019 +0800
@@ -169,6 +169,18 @@
         return sname;
     }
 
+    public Ticket getTicket() throws RealmException {
+        return ticket;
+    }
+
+    public PrincipalName getServicePrincipal2() throws RealmException {
+        return secondTicket == null ? null : secondTicket.sname;
+    }
+
+    public PrincipalName getClientPrincipal() throws RealmException {
+        return cname;
+    }
+
     public sun.security.krb5.Credentials setKrbCreds() {
         // Note: We will not pass authorizationData to s.s.k.Credentials. The
         // field in that class will be passed to Krb5Context as the return
@@ -208,7 +220,15 @@
         return key.getEType();
     }
 
+    public EncryptionKey getKey() {
+        return key;
+    }
+
     public int getTktEType() {
         return ticket.encPart.getEType();
     }
+
+    public int getTktEType2() {
+        return (secondTicket == null) ? 0 : secondTicket.encPart.getEType();
+    }
 }
--- a/src/share/classes/sun/security/krb5/internal/ccache/CredentialsCache.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/krb5/internal/ccache/CredentialsCache.java	Fri Apr 19 10:22:20 2019 +0800
@@ -32,14 +32,9 @@
 
 import sun.security.krb5.*;
 import sun.security.krb5.internal.*;
-import java.util.StringTokenizer;
-import java.util.Vector;
+
+import java.util.List;
 import java.io.IOException;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
 
 /**
  * CredentialsCache stores credentials(tickets, session keys, etc) in a semi-permanent store
@@ -120,6 +115,62 @@
     public abstract void save() throws IOException, KrbException;
     public abstract Credentials[] getCredsList();
     public abstract Credentials getDefaultCreds();
+    public abstract sun.security.krb5.Credentials getInitialCreds();
     public abstract Credentials getCreds(PrincipalName sname);
     public abstract Credentials getCreds(LoginOptions options, PrincipalName sname);
+    public abstract void addConfigEntry(ConfigEntry e);
+    public abstract List<ConfigEntry> getConfigEntries();
+
+    public ConfigEntry getConfigEntry(String name) {
+        List<ConfigEntry> entries = getConfigEntries();
+        if (entries != null) {
+            for (ConfigEntry e : entries) {
+                if (e.getName().equals(name)) {
+                    return e;
+                }
+            }
+        }
+        return null;
+    }
+
+    public static class ConfigEntry {
+
+        public ConfigEntry(String name, PrincipalName princ, byte[] data) {
+            this.name = name;
+            this.princ = princ;
+            this.data = data;
+        }
+
+        private final String name;
+        private final PrincipalName princ;
+        private final byte[] data; // not worth cloning
+
+        public String getName() {
+            return name;
+        }
+
+        public PrincipalName getPrinc() {
+            return princ;
+        }
+
+        public byte[] getData() {
+            return data;
+        }
+
+        @Override
+        public String toString() {
+            return name + (princ != null ? ("." + princ) : "")
+                    + ": " + new String(data);
+        }
+
+        public PrincipalName getSName() {
+            try {
+                return new PrincipalName("krb5_ccache_conf_data/" + name
+                        + (princ != null ? ("/" + princ) : "")
+                        + "@X-CACHECONF:");
+            } catch (RealmException e) {
+                throw new AssertionError(e);
+            }
+        }
+    }
 }
--- a/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java	Fri Apr 19 10:22:20 2019 +0800
@@ -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
@@ -35,6 +35,12 @@
 
 import sun.security.krb5.*;
 import sun.security.krb5.internal.*;
+import sun.security.util.SecurityProperties;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
 import java.util.StringTokenizer;
 import java.util.Vector;
 import java.io.IOException;
@@ -182,9 +188,13 @@
                 primaryPrincipal = p;
             credentialsList = new Vector<Credentials>();
             while (cis.available() > 0) {
-                Credentials cred = cis.readCred(version);
+                Object cred = cis.readCred(version);
                 if (cred != null) {
-                    credentialsList.addElement(cred);
+                    if (cred instanceof Credentials) {
+                        credentialsList.addElement((Credentials)cred);
+                    } else {
+                        addConfigEntry((CredentialsCache.ConfigEntry)cred);
+                    }
                 }
             }
         }
@@ -255,6 +265,9 @@
                     cos.addCreds(tmp[i]);
                 }
             }
+            for (ConfigEntry e : getConfigEntries()) {
+                cos.addConfigEntry(primaryPrincipal, e);
+            }
         }
     }
 
@@ -307,6 +320,17 @@
         }
     }
 
+    private List<ConfigEntry> configEntries = new ArrayList<>();
+
+    @Override
+    public void addConfigEntry(ConfigEntry e) {
+        configEntries.add(e);
+    }
+
+    @Override
+    public List<ConfigEntry> getConfigEntries() {
+        return Collections.unmodifiableList(configEntries);
+    }
 
     /**
      * Gets a credentials for a specified service.
@@ -326,6 +350,81 @@
         return null;
     }
 
+    public sun.security.krb5.Credentials getInitialCreds() {
+
+        Credentials defaultCreds = getDefaultCreds();
+        if (defaultCreds == null) {
+            return null;
+        }
+        sun.security.krb5.Credentials tgt = defaultCreds.setKrbCreds();
+
+        CredentialsCache.ConfigEntry entry = getConfigEntry("proxy_impersonator");
+        if (entry == null) {
+            if (DEBUG) {
+                System.out.println("get normal credential");
+            }
+            return tgt;
+        }
+
+        boolean force;
+        String prop = SecurityProperties.privilegedGetOverridable(
+                "jdk.security.krb5.default.initiate.credential");
+        if (prop == null) {
+            prop = "always-impersonate";
+        }
+        switch (prop) {
+            case "no-impersonate": // never try impersonation
+                if (DEBUG) {
+                    System.out.println("get normal credential");
+                }
+                return tgt;
+            case "try-impersonate":
+                force = false;
+                break;
+            case "always-impersonate":
+                force = true;
+                break;
+            default:
+                throw new RuntimeException(
+                        "Invalid jdk.security.krb5.default.initiate.credential");
+        }
+
+        try {
+            PrincipalName service = new PrincipalName(
+                    new String(entry.getData(), StandardCharsets.UTF_8));
+            if (!tgt.getClient().equals(service)) {
+                if (DEBUG) {
+                    System.out.println("proxy_impersonator does not match service name");
+                }
+                return force ? null : tgt;
+            }
+            PrincipalName client = getPrimaryPrincipal();
+            Credentials proxy = null;
+            for (Credentials c : getCredsList()) {
+                if (c.getClientPrincipal().equals(client)
+                        && c.getServicePrincipal().equals(service)) {
+                    proxy = c;
+                    break;
+                }
+            }
+            if (proxy == null) {
+                if (DEBUG) {
+                    System.out.println("Cannot find evidence ticket in ccache");
+                }
+                return force ? null : tgt;
+            }
+            if (DEBUG) {
+                System.out.println("Get proxied credential");
+            }
+            return tgt.setProxy(proxy.setKrbCreds());
+        } catch (KrbException e) {
+            if (DEBUG) {
+                System.out.println("Impersonation with ccache failed");
+            }
+            return force ? null : tgt;
+        }
+    }
+
     public Credentials getDefaultCreds() {
         Credentials[] list = getCredsList();
         if (list == null) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/share/classes/sun/security/util/SecurityProperties.java	Fri Apr 19 10:22:20 2019 +0800
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2018 SAP SE. 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
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+package sun.security.util;
+
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.Security;
+
+public class SecurityProperties {
+
+    /**
+     * Returns the value of the security property propName, which can be overridden
+     * by a system property of the same name
+     *
+     * @param  propName the name of the system or security property
+     * @return the value of the system or security property
+     */
+    public static String privilegedGetOverridable(String propName) {
+        if (System.getSecurityManager() == null) {
+            return getOverridableProperty(propName);
+        } else {
+            return AccessController.doPrivileged((PrivilegedAction<String>) () -> getOverridableProperty(propName));
+        }
+    }
+
+    private static String getOverridableProperty(String propName) {
+        String val = System.getProperty(propName);
+        if (val == null) {
+            return Security.getProperty(propName);
+        } else {
+            return val;
+        }
+    }
+
+    /**
+     * Returns true in case the system or security property "jdk.includeInExceptions"
+     * contains the category refName
+     *
+     * @param refName the category to check
+     * @return true in case the system or security property "jdk.includeInExceptions"
+     *         contains refName, false otherwise
+     */
+    public static boolean includedInExceptions(String refName) {
+        String val = privilegedGetOverridable("jdk.includeInExceptions");
+        if (val == null) {
+            return false;
+        }
+
+        String[] tokens = val.split(",");
+        for (String token : tokens) {
+            token = token.trim();
+            if (token.equalsIgnoreCase(refName)) {
+                return true;
+            }
+        }
+        return false;
+    }
+}
--- a/src/share/lib/security/java.security-aix	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/lib/security/java.security-aix	Fri Apr 19 10:22:20 2019 +0800
@@ -1005,3 +1005,32 @@
 # if this property is not enabled.
 #
 jdk.security.caDistrustPolicies=SYMANTEC_TLS
+
+#
+# Policies for the proxy_impersonator Kerberos ccache configuration entry
+#
+# The proxy_impersonator ccache configuration entry indicates that the ccache
+# is a synthetic delegated credential for use with S4U2Proxy by an intermediate
+# server. The ccache file should also contain the TGT of this server and
+# an evidence ticket from the default principal of the ccache to this server.
+#
+# This security property determines how Java uses this configuration entry.
+# There are 3 possible values:
+#
+#  no-impersonate     - Ignore this configuration entry, and always act as
+#                       the owner of the TGT (if it exists).
+#
+#  try-impersonate    - Try impersonation when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       fallback to no-impersonate.
+#
+#  always-impersonate - Always impersonate when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       no initial credential is read from the ccache.
+#
+# The default value is "always-impersonate".
+#
+# If a system property of the same name is also specified, it supersedes the
+# security property value defined here.
+#
+#jdk.security.krb5.default.initiate.credential=always-impersonate
\ No newline at end of file
--- a/src/share/lib/security/java.security-linux	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/lib/security/java.security-linux	Fri Apr 19 10:22:20 2019 +0800
@@ -1011,3 +1011,32 @@
 # if this property is not enabled.
 #
 jdk.security.caDistrustPolicies=SYMANTEC_TLS
+
+#
+# Policies for the proxy_impersonator Kerberos ccache configuration entry
+#
+# The proxy_impersonator ccache configuration entry indicates that the ccache
+# is a synthetic delegated credential for use with S4U2Proxy by an intermediate
+# server. The ccache file should also contain the TGT of this server and
+# an evidence ticket from the default principal of the ccache to this server.
+#
+# This security property determines how Java uses this configuration entry.
+# There are 3 possible values:
+#
+#  no-impersonate     - Ignore this configuration entry, and always act as
+#                       the owner of the TGT (if it exists).
+#
+#  try-impersonate    - Try impersonation when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       fallback to no-impersonate.
+#
+#  always-impersonate - Always impersonate when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       no initial credential is read from the ccache.
+#
+# The default value is "always-impersonate".
+#
+# If a system property of the same name is also specified, it supersedes the
+# security property value defined here.
+#
+#jdk.security.krb5.default.initiate.credential=always-impersonate
\ No newline at end of file
--- a/src/share/lib/security/java.security-macosx	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/lib/security/java.security-macosx	Fri Apr 19 10:22:20 2019 +0800
@@ -1009,3 +1009,32 @@
 # if this property is not enabled.
 #
 jdk.security.caDistrustPolicies=SYMANTEC_TLS
+
+#
+# Policies for the proxy_impersonator Kerberos ccache configuration entry
+#
+# The proxy_impersonator ccache configuration entry indicates that the ccache
+# is a synthetic delegated credential for use with S4U2Proxy by an intermediate
+# server. The ccache file should also contain the TGT of this server and
+# an evidence ticket from the default principal of the ccache to this server.
+#
+# This security property determines how Java uses this configuration entry.
+# There are 3 possible values:
+#
+#  no-impersonate     - Ignore this configuration entry, and always act as
+#                       the owner of the TGT (if it exists).
+#
+#  try-impersonate    - Try impersonation when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       fallback to no-impersonate.
+#
+#  always-impersonate - Always impersonate when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       no initial credential is read from the ccache.
+#
+# The default value is "always-impersonate".
+#
+# If a system property of the same name is also specified, it supersedes the
+# security property value defined here.
+#
+#jdk.security.krb5.default.initiate.credential=always-impersonate
\ No newline at end of file
--- a/src/share/lib/security/java.security-solaris	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/lib/security/java.security-solaris	Fri Apr 19 10:22:20 2019 +0800
@@ -1008,3 +1008,32 @@
 # if this property is not enabled.
 #
 jdk.security.caDistrustPolicies=SYMANTEC_TLS
+
+#
+# Policies for the proxy_impersonator Kerberos ccache configuration entry
+#
+# The proxy_impersonator ccache configuration entry indicates that the ccache
+# is a synthetic delegated credential for use with S4U2Proxy by an intermediate
+# server. The ccache file should also contain the TGT of this server and
+# an evidence ticket from the default principal of the ccache to this server.
+#
+# This security property determines how Java uses this configuration entry.
+# There are 3 possible values:
+#
+#  no-impersonate     - Ignore this configuration entry, and always act as
+#                       the owner of the TGT (if it exists).
+#
+#  try-impersonate    - Try impersonation when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       fallback to no-impersonate.
+#
+#  always-impersonate - Always impersonate when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       no initial credential is read from the ccache.
+#
+# The default value is "always-impersonate".
+#
+# If a system property of the same name is also specified, it supersedes the
+# security property value defined here.
+#
+#jdk.security.krb5.default.initiate.credential=always-impersonate
\ No newline at end of file
--- a/src/share/lib/security/java.security-windows	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/share/lib/security/java.security-windows	Fri Apr 19 10:22:20 2019 +0800
@@ -1009,3 +1009,32 @@
 # if this property is not enabled.
 #
 jdk.security.caDistrustPolicies=SYMANTEC_TLS
+
+#
+# Policies for the proxy_impersonator Kerberos ccache configuration entry
+#
+# The proxy_impersonator ccache configuration entry indicates that the ccache
+# is a synthetic delegated credential for use with S4U2Proxy by an intermediate
+# server. The ccache file should also contain the TGT of this server and
+# an evidence ticket from the default principal of the ccache to this server.
+#
+# This security property determines how Java uses this configuration entry.
+# There are 3 possible values:
+#
+#  no-impersonate     - Ignore this configuration entry, and always act as
+#                       the owner of the TGT (if it exists).
+#
+#  try-impersonate    - Try impersonation when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       fallback to no-impersonate.
+#
+#  always-impersonate - Always impersonate when this configuration entry exists.
+#                       If no matching TGT or evidence ticket is found,
+#                       no initial credential is read from the ccache.
+#
+# The default value is "always-impersonate".
+#
+# If a system property of the same name is also specified, it supersedes the
+# security property value defined here.
+#
+#jdk.security.krb5.default.initiate.credential=always-impersonate
\ No newline at end of file
--- a/src/windows/classes/sun/security/krb5/internal/tools/Klist.java	Tue Apr 23 11:59:54 2019 -0700
+++ b/src/windows/classes/sun/security/krb5/internal/tools/Klist.java	Fri Apr 19 10:22:20 2019 +0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 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
@@ -31,6 +31,8 @@
 package sun.security.krb5.internal.tools;
 
 import java.net.InetAddress;
+import java.util.List;
+
 import sun.security.krb5.*;
 import sun.security.krb5.internal.*;
 import sun.security.krb5.internal.ccache.*;
@@ -241,6 +243,8 @@
                     String endtime;
                     String renewTill;
                     String servicePrincipal;
+                    PrincipalName servicePrincipal2;
+                    String clientPrincipal;
                     if (creds[i].getStartTime() != null) {
                         starttime = format(creds[i].getStartTime());
                     } else {
@@ -252,6 +256,18 @@
                     System.out.println("[" + (i + 1) + "] " +
                                        " Service Principal:  " +
                                        servicePrincipal);
+                    servicePrincipal2 =
+                            creds[i].getServicePrincipal2();
+                    if (servicePrincipal2 != null) {
+                        System.out.println("     Second Service:     "
+                                + servicePrincipal2);
+                    }
+                    clientPrincipal =
+                            creds[i].getClientPrincipal().toString();
+                    if (!clientPrincipal.equals(defaultPrincipal)) {
+                        System.out.println("     Client Principal:   " +
+                                clientPrincipal);
+                    }
                     System.out.println("     Valid starting:     " + starttime);
                     System.out.println("     Expires:            " + endtime);
                     if (creds[i].getRenewTill() != null) {
@@ -262,8 +278,15 @@
                     if (options[0] == 'e') {
                         String eskey = EType.toString(creds[i].getEType());
                         String etkt = EType.toString(creds[i].getTktEType());
-                        System.out.println("     EType (skey, tkt):  "
-                                + eskey + ", " + etkt);
+                        if (creds[i].getTktEType2() == 0) {
+                            System.out.println("     EType (skey, tkt):  "
+                                    + eskey + ", " + etkt);
+                        } else {
+                            String etkt2 = EType.toString(creds[i].getTktEType2());
+                            System.out.println("     EType (skey, tkts): "
+                                    + eskey + ", " + etkt
+                                    + ", " + etkt2);
+                        }
                     }
                     if (options[1] == 'f') {
                         System.out.println("     Flags:              " +
@@ -302,6 +325,15 @@
         } else {
             System.out.println("\nNo entries found.");
         }
+
+        List<CredentialsCache.ConfigEntry> configEntries
+                = cache.getConfigEntries();
+        if (configEntries != null && !configEntries.isEmpty()) {
+            System.out.println("\nConfig entries:");
+            for (CredentialsCache.ConfigEntry e : configEntries) {
+                System.out.println("     " + e);
+            }
+        }
     }
 
     void displayMessage(String target) {