From 064200d553495ed0d8af9e489ff37c67918f4eac Mon Sep 17 00:00:00 2001 From: weijun Date: Fri, 19 Apr 2019 10:22:20 +0800 Subject: [PATCH] 8220302: Better Kerberos ccache handling Reviewed-by: valeriep --- .../security/auth/module/Krb5LoginModule.java | 12 +- .../JavaxSecurityAuthKerberosAccessImpl.java | 10 +- .../auth/kerberos/KerberosTicket.java | 20 +++- .../sun/security/jgss/krb5/Krb5Context.java | 8 +- .../jgss/krb5/Krb5InitCredential.java | 12 +- .../security/jgss/krb5/Krb5MechFactory.java | 4 +- .../security/jgss/krb5/Krb5NameElement.java | 4 +- .../jgss/krb5/Krb5ProxyCredential.java | 31 +++++- .../sun/security/jgss/krb5/Krb5Util.java | 29 ++++- .../sun/security/krb5/Credentials.java | 36 +++--- .../krb5/JavaxSecurityAuthKerberosAccess.java | 15 ++- .../classes/sun/security/krb5/Realm.java | 1 - .../internal/ccache/CCacheInputStream.java | 32 ++++-- .../internal/ccache/CCacheOutputStream.java | 16 ++- .../krb5/internal/ccache/Credentials.java | 20 ++++ .../internal/ccache/CredentialsCache.java | 65 +++++++++-- .../internal/ccache/FileCredentialsCache.java | 105 +++++++++++++++++- .../sun/security/util/SecurityProperties.java | 82 ++++++++++++++ src/share/lib/security/java.security-aix | 29 +++++ src/share/lib/security/java.security-linux | 29 +++++ src/share/lib/security/java.security-macosx | 29 +++++ src/share/lib/security/java.security-solaris | 29 +++++ src/share/lib/security/java.security-windows | 29 +++++ .../security/krb5/internal/tools/Klist.java | 38 ++++++- 24 files changed, 614 insertions(+), 71 deletions(-) create mode 100644 src/share/classes/sun/security/util/SecurityProperties.java diff --git a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java index 8539a266e..8547363b1 100644 --- a/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java +++ b/src/share/classes/com/sun/security/auth/module/Krb5LoginModule.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -668,7 +668,11 @@ public class Krb5LoginModule implements LoginModule { // 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 @@ public class Krb5LoginModule implements LoginModule { // 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) { diff --git a/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java b/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java index 9630c72ea..71cc0aa50 100644 --- a/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java +++ b/src/share/classes/javax/security/auth/kerberos/JavaxSecurityAuthKerberosAccessImpl.java @@ -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 @@ class JavaxSecurityAuthKerberosAccessImpl KeyTab ktab) { return ktab.takeSnapshot(); } + public KerberosTicket kerberosTicketGetProxy(KerberosTicket t) { + return t.proxy; + } + public void kerberosTicketSetProxy(KerberosTicket t, KerberosTicket p) { + t.proxy = p; + } } diff --git a/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java b/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java index 21fd23324..0327f04db 100644 --- a/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java +++ b/src/share/classes/javax/security/auth/kerberos/KerberosTicket.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -29,11 +29,13 @@ import java.io.*; 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,9 +192,14 @@ public class KerberosTicket implements Destroyable, Refreshable, * @serial */ - 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 @@ public class KerberosTicket implements Destroyable, Refreshable, "Renew Till = " + String.valueOf(renewTill) + "\n" + "Client Addresses " + (clientAddresses == null ? " Null " : caddrBuf.toString() + + (proxy == null ? "" : "\nwith a proxy ticket") + "\n")); } @@ -699,6 +707,10 @@ public class KerberosTicket implements Destroyable, Refreshable, // 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 @@ public class KerberosTicket implements Destroyable, Refreshable, return false; } + if (!Objects.equals(proxy, otherTicket.proxy)) { + return false; + } + return true; } diff --git a/src/share/classes/sun/security/jgss/krb5/Krb5Context.java b/src/share/classes/sun/security/jgss/krb5/Krb5Context.java index e7c9d6f39..a0658d0ea 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5Context.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5Context.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -614,6 +614,8 @@ class Krb5Context implements GSSContextSpi { 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 @@ class Krb5Context implements GSSContextSpi { // 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 --git a/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java b/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java index cdfbe172e..daefbbcbd 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5InitCredential.java @@ -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 @@ public class Krb5InitCredential private Krb5NameElement name; private Credentials krb5Credentials; + public KerberosTicket proxyTicket; private Krb5InitCredential(Krb5NameElement name, byte[] asn1Encoding, @@ -153,7 +154,7 @@ public class Krb5InitCredential Krb5MechFactory.NT_GSS_KRB5_PRINCIPAL); } - return new Krb5InitCredential(name, + Krb5InitCredential result = new Krb5InitCredential(name, tgt.getEncoded(), tgt.getClient(), tgt.getServer(), @@ -165,6 +166,9 @@ public class Krb5InitCredential tgt.getEndTime(), tgt.getRenewTill(), tgt.getClientAddresses()); + result.proxyTicket = KerberosSecrets.getJavaxSecurityAuthKerberosAccess(). + kerberosTicketGetProxy(tgt); + return result; } static Krb5InitCredential getInstance(Krb5NameElement name, @@ -334,9 +338,9 @@ public class Krb5InitCredential 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 --git a/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java b/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java index ffae2cdc2..4c6f839b1 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5MechFactory.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -120,6 +120,8 @@ public final class Krb5MechFactory implements MechanismFactory { 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 --git a/src/share/classes/sun/security/jgss/krb5/Krb5NameElement.java b/src/share/classes/sun/security/jgss/krb5/Krb5NameElement.java index 8d2c1af99..37d0b77bb 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5NameElement.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5NameElement.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -144,7 +144,7 @@ public class Krb5NameElement 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 --git a/src/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java b/src/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java index 4c5690b39..8fbe93a57 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5ProxyCredential.java @@ -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 @@ public class Krb5ProxyCredential 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 --git a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java index 31b54b725..8f79df1e6 100644 --- a/src/share/classes/sun/security/jgss/krb5/Krb5Util.java +++ b/src/share/classes/sun/security/jgss/krb5/Krb5Util.java @@ -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 @@ public class Krb5Util { /** * 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 @@ public class Krb5Util { 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 --git a/src/share/classes/sun/security/krb5/Credentials.java b/src/share/classes/sun/security/krb5/Credentials.java index 80095ac94..3fe295307 100644 --- a/src/share/classes/sun/security/krb5/Credentials.java +++ b/src/share/classes/sun/security/krb5/Credentials.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -56,13 +56,23 @@ public class Credentials { 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 @@ public class Credentials { 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 @@ public class Credentials { 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 @@ public class Credentials { return cache; } - public EncryptionKey getServiceKey() { - return serviceKey; - } - /* * Prints out debug info. */ diff --git a/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java b/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java index 501e6b309..84ca79bc9 100644 --- a/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java +++ b/src/share/classes/sun/security/krb5/JavaxSecurityAuthKerberosAccess.java @@ -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 interface JavaxSecurityAuthKerberosAccess { */ 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); } diff --git a/src/share/classes/sun/security/krb5/Realm.java b/src/share/classes/sun/security/krb5/Realm.java index b1295e794..2cf7bbfad 100644 --- a/src/share/classes/sun/security/krb5/Realm.java +++ b/src/share/classes/sun/security/krb5/Realm.java @@ -188,7 +188,6 @@ public class Realm implements Cloneable { return false; for (int i = 0; i < name.length(); i++) { if (name.charAt(i) == '/' || - name.charAt(i) == ':' || name.charAt(i) == '\0') { return false; } diff --git a/src/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java b/src/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java index a15e4aac7..c7d9d2ade 100644 --- a/src/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java +++ b/src/share/classes/sun/security/krb5/internal/ccache/CCacheInputStream.java @@ -325,16 +325,13 @@ public class CCacheInputStream extends KrbDataInputStream implements FileCCacheC } /** - * 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 @@ public class CCacheInputStream extends KrbDataInputStream implements FileCCacheC } 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 --git a/src/share/classes/sun/security/krb5/internal/ccache/CCacheOutputStream.java b/src/share/classes/sun/security/krb5/internal/ccache/CCacheOutputStream.java index dcc15e76d..cde9712bf 100644 --- a/src/share/classes/sun/security/krb5/internal/ccache/CCacheOutputStream.java +++ b/src/share/classes/sun/security/krb5/internal/ccache/CCacheOutputStream.java @@ -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 @@ public class CCacheOutputStream extends KrbDataOutputStream implements FileCCach 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 --git a/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java b/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java index 7128545a2..c62e6293f 100644 --- a/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java +++ b/src/share/classes/sun/security/krb5/internal/ccache/Credentials.java @@ -169,6 +169,18 @@ public class Credentials { 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 @@ public class Credentials { 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 --git a/src/share/classes/sun/security/krb5/internal/ccache/CredentialsCache.java b/src/share/classes/sun/security/krb5/internal/ccache/CredentialsCache.java index 8c61ed395..9489b9238 100644 --- a/src/share/classes/sun/security/krb5/internal/ccache/CredentialsCache.java +++ b/src/share/classes/sun/security/krb5/internal/ccache/CredentialsCache.java @@ -32,14 +32,9 @@ package sun.security.krb5.internal.ccache; 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 class CredentialsCache { 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 --git a/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java b/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java index a682dddcb..91828292d 100644 --- a/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java +++ b/src/share/classes/sun/security/krb5/internal/ccache/FileCredentialsCache.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2000, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -35,6 +35,12 @@ package sun.security.krb5.internal.ccache; 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 @@ public class FileCredentialsCache extends CredentialsCache 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 @@ public class FileCredentialsCache extends CredentialsCache cos.addCreds(tmp[i]); } } + for (ConfigEntry e : getConfigEntries()) { + cos.addConfigEntry(primaryPrincipal, e); + } } } @@ -307,6 +320,17 @@ public class FileCredentialsCache extends CredentialsCache } } + 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 @@ public class FileCredentialsCache extends CredentialsCache 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 --git a/src/share/classes/sun/security/util/SecurityProperties.java b/src/share/classes/sun/security/util/SecurityProperties.java new file mode 100644 index 000000000..7a1ca6a25 --- /dev/null +++ b/src/share/classes/sun/security/util/SecurityProperties.java @@ -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) () -> 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; + } +} diff --git a/src/share/lib/security/java.security-aix b/src/share/lib/security/java.security-aix index e2c0ff9b3..69cc59fa6 100644 --- a/src/share/lib/security/java.security-aix +++ b/src/share/lib/security/java.security-aix @@ -1005,3 +1005,32 @@ jceks.key.serialFilter = java.lang.Enum;java.security.KeyRep;\ # 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 diff --git a/src/share/lib/security/java.security-linux b/src/share/lib/security/java.security-linux index 2f33b1acb..ae28ce7b7 100644 --- a/src/share/lib/security/java.security-linux +++ b/src/share/lib/security/java.security-linux @@ -1011,3 +1011,32 @@ jceks.key.serialFilter = java.lang.Enum;java.security.KeyRep;\ # 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 diff --git a/src/share/lib/security/java.security-macosx b/src/share/lib/security/java.security-macosx index 277c3356d..8af804b71 100644 --- a/src/share/lib/security/java.security-macosx +++ b/src/share/lib/security/java.security-macosx @@ -1009,3 +1009,32 @@ jceks.key.serialFilter = java.lang.Enum;java.security.KeyRep;\ # 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 diff --git a/src/share/lib/security/java.security-solaris b/src/share/lib/security/java.security-solaris index 27ce9669e..204871b14 100644 --- a/src/share/lib/security/java.security-solaris +++ b/src/share/lib/security/java.security-solaris @@ -1008,3 +1008,32 @@ jceks.key.serialFilter = java.lang.Enum;java.security.KeyRep;\ # 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 diff --git a/src/share/lib/security/java.security-windows b/src/share/lib/security/java.security-windows index 58aad3a1d..e49d1880a 100644 --- a/src/share/lib/security/java.security-windows +++ b/src/share/lib/security/java.security-windows @@ -1009,3 +1009,32 @@ jceks.key.serialFilter = java.lang.Enum;java.security.KeyRep;\ # 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 diff --git a/src/windows/classes/sun/security/krb5/internal/tools/Klist.java b/src/windows/classes/sun/security/krb5/internal/tools/Klist.java index ca5a473cb..37f59adae 100644 --- a/src/windows/classes/sun/security/krb5/internal/tools/Klist.java +++ b/src/windows/classes/sun/security/krb5/internal/tools/Klist.java @@ -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 @@ public class Klist { 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 @@ public class Klist { 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 @@ public class Klist { 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 @@ public class Klist { } 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) { -- GitLab