# HG changeset patch # User weijun # Date 1555640540 -28800 # Node ID 53ddf218eddd460a71dc269d1b5a70937f3ef934 # Parent 94710bb2a5bb44ea3dd39a1ac224bec653825c64 8220302: Better Kerberos ccache handling Reviewed-by: valeriep diff -r 94710bb2a5bb -r 53ddf218eddd src/java.base/share/conf/security/java.security --- a/src/java.base/share/conf/security/java.security Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.base/share/conf/security/java.security Fri Apr 19 10:22:20 2019 +0800 @@ -1213,3 +1213,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 diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java Fri Apr 19 10:22:20 2019 +0800 @@ -49,4 +49,12 @@ public void kerberosTicketSetServerAlias(KerberosTicket t, KerberosPrincipal a) { t.serverAlias = a; } + + public KerberosTicket kerberosTicketGetProxy(KerberosTicket t) { + return t.proxy; + } + + public void kerberosTicketSetProxy(KerberosTicket t, KerberosTicket p) { + t.proxy = p; + } } diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java --- a/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/share/classes/javax/security/auth/kerberos/KerberosTicket.java Fri Apr 19 10:22:20 2019 +0800 @@ -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.security.util.HexDumpEncoder; /** @@ -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; @@ -711,6 +718,7 @@ "Renew Till = " + String.valueOf(renewTill) + "\n" + "Client Addresses " + (clientAddresses == null ? " Null " : caddrString.toString() + + (proxy == null ? "" : "\nwith a proxy ticket") + "\n")); } @@ -748,6 +756,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); } @@ -820,6 +832,10 @@ } } + if (!Objects.equals(proxy, otherTicket.proxy)) { + return false; + } + return true; } diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Context.java Fri Apr 19 10:22:20 2019 +0800 @@ -617,6 +617,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"); @@ -653,8 +655,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 diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java Fri Apr 19 10:22:20 2019 +0800 @@ -55,6 +55,7 @@ private Krb5NameElement name; private Credentials krb5Credentials; + public KerberosTicket proxyTicket; private Krb5InitCredential(Krb5NameElement name, byte[] asn1Encoding, @@ -173,7 +174,7 @@ KerberosPrincipal serverAlias = KerberosSecrets .getJavaxSecurityAuthKerberosAccess() .kerberosTicketGetServerAlias(tgt); - return new Krb5InitCredential(name, + Krb5InitCredential result = new Krb5InitCredential(name, tgt.getEncoded(), tgt.getClient(), clientAlias, @@ -187,6 +188,9 @@ tgt.getEndTime(), tgt.getRenewTill(), tgt.getClientAddresses()); + result.proxyTicket = KerberosSecrets.getJavaxSecurityAuthKerberosAccess(). + kerberosTicketGetProxy(tgt); + return result; } static Krb5InitCredential getInstance(Krb5NameElement name, @@ -369,9 +373,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 = diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/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 @@ -124,6 +124,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) { diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5NameElement.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/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); diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/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"); + } + } } diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java Fri Apr 19 10:22:20 2019 +0800 @@ -60,11 +60,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 { @@ -74,11 +71,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; } diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/Credentials.java Fri Apr 19 10:22:20 2019 +0800 @@ -59,13 +59,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); @@ -361,20 +371,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; } @@ -409,20 +418,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()); } } } @@ -499,10 +507,6 @@ return cache; } - public EncryptionKey getServiceKey() { - return serviceKey; - } - /* * Prints out debug info. */ diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java Fri Apr 19 10:22:20 2019 +0800 @@ -28,8 +28,6 @@ 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; /** * An unsafe tunnel to get non-public access to classes in the @@ -49,4 +47,13 @@ public KerberosPrincipal kerberosTicketGetServerAlias(KerberosTicket t); public void kerberosTicketSetServerAlias(KerberosTicket t, KerberosPrincipal a); + /** + * Returns the proxy for a KerberosTicket. + */ + public KerberosTicket kerberosTicketGetProxy(KerberosTicket t); + + /** + * Sets the proxy for a KerberosTicket. + */ + public void kerberosTicketSetProxy(KerberosTicket t, KerberosTicket p); } diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/krb5/Realm.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/Realm.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/Realm.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 @@ -186,7 +186,6 @@ return false; for (int i = 0; i < name.length(); i++) { if (name.charAt(i) == '/' || - name.charAt(i) == ':' || name.charAt(i) == '\0') { return false; } diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/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; } } diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/CCacheOutputStream.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/CCacheOutputStream.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/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); diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/Credentials.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/Credentials.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/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 @@ -209,7 +221,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(); + } } diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/CredentialsCache.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/CredentialsCache.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/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 getConfigEntries(); + + public ConfigEntry getConfigEntry(String name) { + List 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); + } + } + } } diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java --- a/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.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 @@ -36,6 +36,12 @@ import sun.security.action.GetPropertyAction; 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(); 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 configEntries = new ArrayList<>(); + + @Override + public void addConfigEntry(ConfigEntry e) { + configEntries.add(e); + } + + @Override + public List 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) { diff -r 94710bb2a5bb -r 53ddf218eddd src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Klist.java --- a/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Klist.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/java.security.jgss/windows/classes/sun/security/krb5/internal/tools/Klist.java Fri Apr 19 10:22:20 2019 +0800 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2003, 2018, 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.*; @@ -249,6 +251,8 @@ String endtime; String renewTill; String servicePrincipal; + PrincipalName servicePrincipal2; + String clientPrincipal; if (creds[i].getStartTime() != null) { starttime = format(creds[i].getStartTime()); } else { @@ -260,6 +264,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) { @@ -270,8 +286,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: " + @@ -310,6 +333,15 @@ } else { System.out.println("\nNo entries found."); } + + List 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) { diff -r 94710bb2a5bb -r 53ddf218eddd src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java --- a/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.java Mon Mar 25 17:15:27 2019 +0000 +++ b/src/jdk.security.auth/share/classes/com/sun/security/auth/module/Krb5LoginModule.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 @@ -644,6 +644,7 @@ // renew if ticket is old. Credentials newCred = renewCredentials(cred); if (newCred != null) { + newCred.setProxy(cred.getProxy()); cred = newCred; } } @@ -1070,6 +1071,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) {