diff --git a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java index 24353cceeca..b4e330f3ab7 100644 --- a/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java +++ b/src/java.security.jgss/share/classes/sun/security/jgss/krb5/Krb5Util.java @@ -40,6 +40,8 @@ import sun.security.krb5.Credentials; import sun.security.krb5.EncryptionKey; import sun.security.krb5.KrbException; import java.io.IOException; +import java.util.Objects; + import sun.security.krb5.KerberosSecrets; import sun.security.krb5.PrincipalName; @@ -75,6 +77,11 @@ public class Krb5Util { return ticket; } + // A one slot local ccache for krb5 login. This is only used when + // useSubjectCredsOnly is false. + static String lastClient = null; + static KerberosTicket lastTicket = null; + /** * Retrieves the initial TGT corresponding to the client principal * from the Subject in the specified AccessControlContext. @@ -82,7 +89,7 @@ public class Krb5Util { * useSubjectCredsOnly is false, then obtain ticket from * a LoginContext. */ - static KerberosTicket getInitialTicket(GSSCaller caller, + static synchronized KerberosTicket getInitialTicket(GSSCaller caller, String clientPrincipal, @SuppressWarnings("removal") AccessControlContext acc) throws LoginException { @@ -95,9 +102,38 @@ public class Krb5Util { // 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, - null, clientPrincipal, KerberosTicket.class); + if (Objects.equals(clientPrincipal, lastClient) + && lastTicket != null) { + if (lastTicket.isCurrent()) { + if (DEBUG) { + System.out.println("getInitialTicket: use cached ticket"); + } + ticket = lastTicket; + } else if (lastTicket.isRenewable()) { + try { + lastTicket.refresh(); + if (DEBUG) { + System.out.println("getInitialTicket: renew cached ticket"); + } + ticket = lastTicket; + } catch (Exception e) { + if (DEBUG) { + System.out.println("getInitialTicket: renew cached ticket failed"); + } + } + } + } + + if (ticket == null) { + Subject subject = GSSUtil.login(caller, GSSUtil.GSS_KRB5_MECH_OID); + ticket = SubjectComber.find(subject, + null, clientPrincipal, KerberosTicket.class); + lastClient = clientPrincipal; + lastTicket = ticket; + if (DEBUG) { + System.out.println("getInitialTicket: retrieve ticket from KDC"); + } + } } return ticket; } diff --git a/test/jdk/sun/security/krb5/auto/MultiMechsLoginOnce.java b/test/jdk/sun/security/krb5/auto/MultiMechsLoginOnce.java new file mode 100644 index 00000000000..a089801f965 --- /dev/null +++ b/test/jdk/sun/security/krb5/auto/MultiMechsLoginOnce.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2021, 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 + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * 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. + */ + +/* + * @test + * @bug 6859027 + * @summary Duplicate communications to KDC in GSSManager.createCredential(usage) + * @library /test/lib + * @compile -XDignore.symbol.file MultiMechsLoginOnce.java + * @run main jdk.test.lib.FileInstaller TestHosts TestHosts + * @run main/othervm -Djdk.net.hosts.file=TestHosts MultiMechsLoginOnce me + * @run main/othervm -Djdk.net.hosts.file=TestHosts MultiMechsLoginOnce null + */ + +import org.ietf.jgss.GSSCredential; +import org.ietf.jgss.GSSManager; +import org.ietf.jgss.GSSName; +import org.ietf.jgss.Oid; + +public class MultiMechsLoginOnce { + + static int count = 0; + + public static void main(String[] args) throws Exception { + + new OneKDC(null) { + @Override + protected byte[] processAsReq(byte[] in) throws Exception { + count++; + return super.processAsReq(in); + } + }.writeJAASConf() + .setOption(KDC.Option.PREAUTH_REQUIRED, false); + + System.setProperty("javax.security.auth.useSubjectCredsOnly", "false"); + GSSManager man = GSSManager.getInstance(); + + // Test both with name and without name + GSSName me = args[0].equals("me") + ? man.createName(OneKDC.USER, GSSName.NT_USER_NAME) + : null; + GSSCredential cred = man.createCredential( + me, + GSSCredential.DEFAULT_LIFETIME, + (Oid[])null, + GSSCredential.INITIATE_ONLY); + if (cred.getMechs().length < 2) { + throw new RuntimeException("Not multi mech: " + cred); + } + if (count != 1) { + throw new RuntimeException("Request not once: " + count); + } + } +}