diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java index 43c1201b338..1561fc5b893 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientHandshaker.java @@ -44,8 +44,6 @@ import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.*; -import javax.security.auth.Subject; - import sun.security.ssl.HandshakeMessage.*; import static sun.security.ssl.CipherSuite.KeyExchange.*; @@ -234,7 +232,8 @@ final class ClientHandshaker extends Handshaker { case HandshakeMessage.ht_certificate: if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON - || keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) { + || ClientKeyExchangeService.find(keyExchange.name) != null) { + // No external key exchange provider needs a cert now. fatalSE(Alerts.alert_unexpected_message, "unexpected server cert chain"); // NOTREACHED @@ -333,13 +332,9 @@ final class ClientHandshaker extends Handshaker { throw new SSLProtocolException( "Protocol violation: server sent a server key exchange" + " message for key exchange " + keyExchange); - case K_KRB5: - case K_KRB5_EXPORT: - throw new SSLProtocolException( - "unexpected receipt of server key exchange algorithm"); default: throw new SSLProtocolException( - "unsupported key exchange algorithm = " + "unsupported or unexpected key exchange algorithm = " + keyExchange); } break; @@ -350,10 +345,11 @@ final class ClientHandshaker extends Handshaker { throw new SSLHandshakeException( "Client authentication requested for "+ "anonymous cipher suite."); - } else if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) { + } else if (ClientKeyExchangeService.find(keyExchange.name) != null) { + // No external key exchange provider needs a cert now. throw new SSLHandshakeException( "Client certificate requested for "+ - "kerberos cipher suite."); + "external cipher suite: " + keyExchange); } certRequest = new CertificateRequest(input, protocolVersion); if (debug != null && Debug.isOn("handshake")) { @@ -626,45 +622,17 @@ final class ClientHandshaker extends Handshaker { } // validate subject identity - if (sessionSuite.keyExchange == K_KRB5 || - sessionSuite.keyExchange == K_KRB5_EXPORT) { + ClientKeyExchangeService p = + ClientKeyExchangeService.find(sessionSuite.keyExchange.name); + if (p != null) { Principal localPrincipal = session.getLocalPrincipal(); - Subject subject = null; - try { - subject = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - @Override - public Subject run() throws Exception { - return Krb5Helper.getClientSubject(getAccSE()); - }}); - } catch (PrivilegedActionException e) { - subject = null; - if (debug != null && Debug.isOn("session")) { - System.out.println("Attempt to obtain" + - " subject failed!"); - } - } - - if (subject != null) { - // Eliminate dependency on KerberosPrincipal - Set principals = - subject.getPrincipals(Principal.class); - if (!principals.contains(localPrincipal)) { - throw new SSLProtocolException("Server resumed" + - " session with wrong subject identity"); - } else { - if (debug != null && Debug.isOn("session")) - System.out.println("Subject identity is same"); - } - } else { + if (p.isRelated(true, getAccSE(), localPrincipal)) { if (debug != null && Debug.isOn("session")) - System.out.println("Kerberos credentials are not" + - " present in the current Subject; check if " + - " javax.security.auth.useSubjectAsCreds" + - " system property has been set to false"); - throw new SSLProtocolException - ("Server resumed session with no subject"); + System.out.println("Subject identity is same"); + } else { + throw new SSLProtocolException("Server resumed" + + " session with wrong subject identity or no subject"); } } @@ -1012,8 +980,14 @@ final class ClientHandshaker extends Handshaker { ecdh = new ECDHCrypt(params, sslContext.getSecureRandom()); m2 = new ECDHClientKeyExchange(ecdh.getPublicKey()); break; - case K_KRB5: - case K_KRB5_EXPORT: + default: + ClientKeyExchangeService p = + ClientKeyExchangeService.find(keyExchange.name); + if (p == null) { + // somethings very wrong + throw new RuntimeException + ("Unsupported key exchange: " + keyExchange); + } String sniHostname = null; for (SNIServerName serverName : requestedServerNames) { if (serverName instanceof SNIHostName) { @@ -1022,13 +996,13 @@ final class ClientHandshaker extends Handshaker { } } - KerberosClientKeyExchange kerberosMsg = null; + ClientKeyExchange exMsg = null; if (sniHostname != null) { // use first requested SNI hostname try { - kerberosMsg = new KerberosClientKeyExchange( - sniHostname, getAccSE(), protocolVersion, - sslContext.getSecureRandom()); + exMsg = p.createClientExchange( + sniHostname, getAccSE(), protocolVersion, + sslContext.getSecureRandom()); } catch(IOException e) { if (serverNamesAccepted) { // server accepted requested SNI hostname, @@ -1044,26 +1018,22 @@ final class ClientHandshaker extends Handshaker { } } - if (kerberosMsg == null) { + if (exMsg == null) { String hostname = getHostSE(); if (hostname == null) { throw new IOException("Hostname is required" + - " to use Kerberos cipher suites"); + " to use " + keyExchange + " key exchange"); } - kerberosMsg = new KerberosClientKeyExchange( - hostname, getAccSE(), protocolVersion, - sslContext.getSecureRandom()); + exMsg = p.createClientExchange( + hostname, getAccSE(), protocolVersion, + sslContext.getSecureRandom()); } // Record the principals involved in exchange - session.setPeerPrincipal(kerberosMsg.getPeerPrincipal()); - session.setLocalPrincipal(kerberosMsg.getLocalPrincipal()); - m2 = kerberosMsg; + session.setPeerPrincipal(exMsg.getPeerPrincipal()); + session.setLocalPrincipal(exMsg.getLocalPrincipal()); + m2 = exMsg; break; - default: - // somethings very wrong - throw new RuntimeException - ("Unsupported key exchange: " + keyExchange); } if (debug != null && Debug.isOn("handshake")) { m2.print(System.out); @@ -1094,13 +1064,6 @@ final class ClientHandshaker extends Handshaker { case K_RSA_EXPORT: preMasterSecret = ((RSAClientKeyExchange)m2).preMaster; break; - case K_KRB5: - case K_KRB5_EXPORT: - byte[] secretBytes = - ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret(); - preMasterSecret = new SecretKeySpec(secretBytes, - "TlsPremasterSecret"); - break; case K_DHE_RSA: case K_DHE_DSS: case K_DH_ANON: @@ -1116,8 +1079,13 @@ final class ClientHandshaker extends Handshaker { preMasterSecret = ecdh.getAgreedSecret(serverKey); break; default: - throw new IOException("Internal error: unknown key exchange " - + keyExchange); + if (ClientKeyExchangeService.find(keyExchange.name) != null) { + preMasterSecret = + ((ClientKeyExchange) m2).clientKeyExchange(); + } else { + throw new IOException("Internal error: unknown key exchange " + + keyExchange); + } } calculateKeys(preMasterSecret, null); diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java new file mode 100644 index 00000000000..8b066938c89 --- /dev/null +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchange.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2003, 2013, 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. 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.ssl; + +import javax.crypto.SecretKey; +import java.io.IOException; +import java.io.PrintStream; +import java.security.Principal; + +/** + * Models a non-certificate based ClientKeyExchange + */ +public abstract class ClientKeyExchange extends HandshakeMessage { + + public ClientKeyExchange() { + } + + @Override + int messageType() { + return ht_client_key_exchange; + } + + @Override + abstract public int messageLength(); + + @Override + abstract public void send(HandshakeOutStream s) throws IOException; + + @Override + abstract public void print(PrintStream s) throws IOException; + + abstract public SecretKey clientKeyExchange(); + + abstract public Principal getPeerPrincipal(); + + abstract public Principal getLocalPrincipal(); +} diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java new file mode 100644 index 00000000000..d68f0fad056 --- /dev/null +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ClientKeyExchangeService.java @@ -0,0 +1,141 @@ +/* + * Copyright (c) 2015, 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. 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.ssl; + +import sun.security.action.GetPropertyAction; + +import java.io.File; +import java.io.FilePermission; +import java.io.IOException; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.SecureRandom; +import java.util.*; + +/** + * Models a service that provides support for a particular client key exchange + * mode. Currently used to implement Kerberos-related cipher suites. + * + * @since 1.9 + */ +public interface ClientKeyExchangeService { + + static class Loader { + private static final Map + providers = new HashMap<>(); + + static { + final String key = "java.home"; + String path = AccessController.doPrivileged( + new GetPropertyAction(key), null, + new PropertyPermission(key, "read")); + ServiceLoader sc = + AccessController.doPrivileged( + (PrivilegedAction>) + () -> ServiceLoader.loadInstalled(ClientKeyExchangeService.class), + null, + new FilePermission(new File(path, "-").toString(), "read")); + Iterator iter = sc.iterator(); + while (iter.hasNext()) { + ClientKeyExchangeService cs = iter.next(); + for (String ex: cs.supported()) { + providers.put(ex, cs); + } + } + } + + } + + public static ClientKeyExchangeService find(String ex) { + return Loader.providers.get(ex); + } + + + /** + * Returns the supported key exchange modes by this provider. + * @return the supported key exchange modes + */ + String[] supported(); + + /** + * Returns a generalized credential object on the server side. The server + * side can use the info to determine if a cipher suite can be enabled. + * @param acc the AccessControlContext of the SSL session + * @return the credential object + */ + Object getServiceCreds(AccessControlContext acc); + + /** + * Returns the host name for a service principal. The info can be used in + * SNI or host name verifier. + * @param principal the principal of a service + * @return the string formed host name + */ + String getServiceHostName(Principal principal); + + /** + * Returns whether the specified principal is related to the current + * SSLSession. The info can be used to verify a SSL resume. + * @param isClient if true called from client side, otherwise from server + * @param acc the AccessControlContext of the SSL session + * @param p the specified principal + * @return true if related + */ + boolean isRelated(boolean isClient, AccessControlContext acc, Principal p); + + /** + * Creates the ClientKeyExchange object on the client side. + * @param serverName the intented peer name + * @param acc the AccessControlContext of the SSL session + * @param protocolVersion the TLS protocol version + * @param rand the SecureRandom that will used to generate the premaster + * @return the new Exchanger object + * @throws IOException if there is an error + */ + ClientKeyExchange createClientExchange(String serverName, AccessControlContext acc, + ProtocolVersion protocolVersion, SecureRandom rand) throws IOException; + + /** + * Create the ClientKeyExchange on the server side. + * @param protocolVersion the protocol version + * @param clientVersion the input protocol version + * @param rand a SecureRandom object used to generate premaster + * (if the server has to create one) + * @param encodedTicket the ticket from client + * @param encrypted the encrypted premaster secret from client + * @param acc the AccessControlContext of the SSL session + * @param ServiceCreds the service side credentials object as retrived from + * {@link #getServiceCreds} + * @return the new Exchanger object + * @throws IOException if there is an error + */ + ClientKeyExchange createServerExchange( + ProtocolVersion protocolVersion, ProtocolVersion clientVersion, + SecureRandom rand, byte[] encodedTicket, byte[] encrypted, + AccessControlContext acc, Object ServiceCreds) throws IOException; +} diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java index 62f8acd4614..d8c5c2699cf 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/JsseJce.java @@ -64,24 +64,9 @@ final class JsseJce { // If true, then all the Kerberos-based crypto we need is available. private final static boolean kerberosAvailable; static { - boolean temp; - try { - AccessController.doPrivileged( - new PrivilegedExceptionAction() { - @Override - public Void run() throws Exception { - // Test for Kerberos using the bootstrap class loader - Class.forName("sun.security.krb5.PrincipalName", true, - null); - return null; - } - }); - temp = true; - - } catch (Exception e) { - temp = false; - } - kerberosAvailable = temp; + ClientKeyExchangeService p = + ClientKeyExchangeService.find("KRB5"); + kerberosAvailable = (p != null); } static { diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/KerberosClientKeyExchange.java b/jdk/src/java.base/share/classes/sun/security/ssl/KerberosClientKeyExchange.java deleted file mode 100644 index ff393d9a5c3..00000000000 --- a/jdk/src/java.base/share/classes/sun/security/ssl/KerberosClientKeyExchange.java +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (c) 2003, 2013, 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. 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.ssl; - -import java.io.IOException; -import java.io.PrintStream; -import java.security.AccessController; -import java.security.AccessControlContext; -import java.security.Principal; -import java.security.PrivilegedAction; -import java.security.SecureRandom; -import javax.crypto.SecretKey; - -/** - * A helper class that calls the KerberosClientKeyExchange implementation. - */ -public class KerberosClientKeyExchange extends HandshakeMessage { - - private static final String IMPL_CLASS = - "sun.security.ssl.krb5.KerberosClientKeyExchangeImpl"; - - private static final Class implClass = AccessController.doPrivileged( - new PrivilegedAction>() { - @Override - public Class run() { - try { - return Class.forName(IMPL_CLASS, true, null); - } catch (ClassNotFoundException cnf) { - return null; - } - } - } - ); - private final KerberosClientKeyExchange impl = createImpl(); - - private KerberosClientKeyExchange createImpl() { - if (implClass != null && - getClass() == KerberosClientKeyExchange.class) { - try { - return (KerberosClientKeyExchange)implClass.newInstance(); - } catch (InstantiationException e) { - throw new AssertionError(e); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - } - return null; - } - - // This constructor will be called when constructing an instance of its - // subclass -- KerberosClientKeyExchangeImpl. Please won't check the - // value of impl variable in this constructor. - protected KerberosClientKeyExchange() { - // please won't check the value of impl variable - } - - public KerberosClientKeyExchange(String serverName, - AccessControlContext acc, ProtocolVersion protocolVersion, - SecureRandom rand) throws IOException { - - if (impl != null) { - init(serverName, acc, protocolVersion, rand); - } else { - throw new IllegalStateException("Kerberos is unavailable"); - } - } - - public KerberosClientKeyExchange(ProtocolVersion protocolVersion, - ProtocolVersion clientVersion, SecureRandom rand, - HandshakeInStream input, AccessControlContext acc, - Object serverKeys) throws IOException { - - if (impl != null) { - init(protocolVersion, clientVersion, rand, input, acc, serverKeys); - } else { - throw new IllegalStateException("Kerberos is unavailable"); - } - } - - @Override - int messageType() { - return ht_client_key_exchange; - } - - @Override - public int messageLength() { - return impl.messageLength(); - } - - @Override - public void send(HandshakeOutStream s) throws IOException { - impl.send(s); - } - - @Override - public void print(PrintStream p) throws IOException { - impl.print(p); - } - - public void init(String serverName, - AccessControlContext acc, ProtocolVersion protocolVersion, - SecureRandom rand) throws IOException { - - if (impl != null) { - impl.init(serverName, acc, protocolVersion, rand); - } - } - - public void init(ProtocolVersion protocolVersion, - ProtocolVersion clientVersion, SecureRandom rand, - HandshakeInStream input, AccessControlContext acc, - Object ServiceCreds) throws IOException { - - if (impl != null) { - impl.init(protocolVersion, clientVersion, - rand, input, acc, ServiceCreds); - } - } - - public byte[] getUnencryptedPreMasterSecret() { - return impl.getUnencryptedPreMasterSecret(); - } - - public Principal getPeerPrincipal(){ - return impl.getPeerPrincipal(); - } - - public Principal getLocalPrincipal(){ - return impl.getLocalPrincipal(); - } -} diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Helper.java b/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Helper.java deleted file mode 100644 index c16eae18aa1..00000000000 --- a/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Helper.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2009, 2013, 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. 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.ssl; - -import java.security.AccessControlContext; -import java.security.AccessController; -import java.security.Permission; -import java.security.Principal; -import java.security.PrivilegedAction; -import javax.crypto.SecretKey; -import javax.security.auth.Subject; -import javax.security.auth.login.LoginException; - -/** - * A helper class for Kerberos APIs. - */ -public final class Krb5Helper { - - private Krb5Helper() { } - - // loads Krb5Proxy implementation class if available - private static final String IMPL_CLASS = - "sun.security.ssl.krb5.Krb5ProxyImpl"; - - private static final Krb5Proxy proxy = - AccessController.doPrivileged(new PrivilegedAction() { - @Override - public Krb5Proxy run() { - try { - Class c = Class.forName(IMPL_CLASS, true, null); - return (Krb5Proxy)c.newInstance(); - } catch (ClassNotFoundException cnf) { - return null; - } catch (InstantiationException e) { - throw new AssertionError(e); - } catch (IllegalAccessException e) { - throw new AssertionError(e); - } - }}); - - /** - * Returns true if Kerberos is available. - */ - public static boolean isAvailable() { - return proxy != null; - } - - private static void ensureAvailable() { - if (proxy == null) - throw new AssertionError("Kerberos should have been available"); - } - - /** - * Returns the Subject associated with client-side of the SSL socket. - */ - public static Subject getClientSubject(AccessControlContext acc) - throws LoginException { - ensureAvailable(); - return proxy.getClientSubject(acc); - } - - /** - * Returns the Subject associated with server-side of the SSL socket. - */ - public static Subject getServerSubject(AccessControlContext acc) - throws LoginException { - ensureAvailable(); - return proxy.getServerSubject(acc); - } - - /** - * Returns the KerberosKeys for the default server-side principal. - */ - public static Object getServiceCreds(AccessControlContext acc) - throws LoginException { - ensureAvailable(); - return proxy.getServiceCreds(acc); - } - - /** - * Returns the server-side principal name associated with the KerberosKey. - */ - public static String getServerPrincipalName(Object serviceCreds) { - ensureAvailable(); - return proxy.getServerPrincipalName(serviceCreds); - } - - /** - * Returns the hostname embedded in the principal name. - */ - public static String getPrincipalHostName(Principal principal) { - ensureAvailable(); - return proxy.getPrincipalHostName(principal); - } - - /** - * Returns a ServicePermission for the principal name and action. - */ - public static Permission getServicePermission(String principalName, - String action) { - ensureAvailable(); - return proxy.getServicePermission(principalName, action); - } - - /** - * Determines if the Subject might contain creds for princ. - */ - public static boolean isRelated(Subject subject, Principal princ) { - ensureAvailable(); - return proxy.isRelated(subject, princ); - } -} diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Proxy.java b/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Proxy.java deleted file mode 100644 index 8596d86a20b..00000000000 --- a/jdk/src/java.base/share/classes/sun/security/ssl/Krb5Proxy.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright (c) 2009, 2013, 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. 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.ssl; - -import java.security.AccessControlContext; -import java.security.Permission; -import java.security.Principal; -import javax.crypto.SecretKey; -import javax.security.auth.Subject; -import javax.security.auth.login.LoginException; - -/** - * An interface to a subset of the Kerberos APIs to avoid a static dependency - * on the types defined by these APIs. - */ -public interface Krb5Proxy { - - /** - * Returns the Subject associated with the client-side of the SSL socket. - */ - Subject getClientSubject(AccessControlContext acc) throws LoginException; - - /** - * Returns the Subject associated with the server-side of the SSL socket. - */ - Subject getServerSubject(AccessControlContext acc) throws LoginException; - - - /** - * Returns the Kerberos ServiceCreds for the default server-side principal. - */ - Object getServiceCreds(AccessControlContext acc) throws LoginException; - - /** - * Returns the server-side principal name associated with the KerberosKey. - */ - String getServerPrincipalName(Object serviceCreds); - - /** - * Returns the hostname embedded in the principal name. - */ - String getPrincipalHostName(Principal principal); - - /** - * Returns a ServicePermission for the principal name and action. - */ - Permission getServicePermission(String principalName, String action); - - /** - * Determines if the Subject might contain creds for princ. - */ - boolean isRelated(Subject subject, Principal princ); -} diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java b/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java index 80cc7a5fae6..225fa71b870 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLAlgorithmConstraints.java @@ -352,18 +352,13 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints { components.add("ECDH_ANON"); } break; - case K_KRB5: - if (!forCertPathOnly) { - components.add("KRB5"); - } - break; - case K_KRB5_EXPORT: - if (!forCertPathOnly) { - components.add("KRB5_EXPORT"); - } - break; default: - // ignore + if (ClientKeyExchangeService.find(keyExchange.name) != null) { + if (!forCertPathOnly) { + components.add(keyExchange.name); + } + } + // otherwise ignore } return components; diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java index 53f841140ef..f4a3f7f1b67 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/SSLSessionImpl.java @@ -425,10 +425,9 @@ final class SSLSessionImpl extends ExtendedSSLSession { // change record of peer identity even by accident, much // less do it intentionally. // - if ((cipherSuite.keyExchange == K_KRB5) || - (cipherSuite.keyExchange == K_KRB5_EXPORT)) { + if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { throw new SSLPeerUnverifiedException("no certificates expected" - + " for Kerberos cipher suites"); + + " for " + cipherSuite.keyExchange + " cipher suites"); } if (peerCerts == null) { throw new SSLPeerUnverifiedException("peer not authenticated"); @@ -481,10 +480,9 @@ final class SSLSessionImpl extends ExtendedSSLSession { // change record of peer identity even by accident, much // less do it intentionally. // - if ((cipherSuite.keyExchange == K_KRB5) || - (cipherSuite.keyExchange == K_KRB5_EXPORT)) { + if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { throw new SSLPeerUnverifiedException("no certificates expected" - + " for Kerberos cipher suites"); + + " for " + cipherSuite.keyExchange + " cipher suites"); } if (peerCerts == null) { throw new SSLPeerUnverifiedException("peer not authenticated"); @@ -522,10 +520,9 @@ final class SSLSessionImpl extends ExtendedSSLSession { * change record of peer identity even by accident, much * less do it intentionally. */ - if ((cipherSuite.keyExchange == K_KRB5) || - (cipherSuite.keyExchange == K_KRB5_EXPORT)) { + if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { throw new SSLPeerUnverifiedException("no certificates expected" - + " for Kerberos cipher suites"); + + " for " + cipherSuite.keyExchange + " cipher suites"); } if (peerCerts != null) { return peerCerts.clone(); @@ -540,7 +537,7 @@ final class SSLSessionImpl extends ExtendedSSLSession { * * @return the peer's principal. Returns an X500Principal of the * end-entity certificate for X509-based cipher suites, and - * Principal for Kerberos cipher suites. + * Principal for Kerberos cipher suites, etc. * * @throws SSLPeerUnverifiedException if the peer's identity has not * been verified @@ -549,12 +546,10 @@ final class SSLSessionImpl extends ExtendedSSLSession { public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - if ((cipherSuite.keyExchange == K_KRB5) || - (cipherSuite.keyExchange == K_KRB5_EXPORT)) { + if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { if (peerPrincipal == null) { throw new SSLPeerUnverifiedException("peer not authenticated"); } else { - // Eliminate dependency on KerberosPrincipal return peerPrincipal; } } @@ -569,15 +564,13 @@ final class SSLSessionImpl extends ExtendedSSLSession { * * @return the principal sent to the peer. Returns an X500Principal * of the end-entity certificate for X509-based cipher suites, and - * Principal for Kerberos cipher suites. If no principal was + * Principal for Kerberos cipher suites, etc. If no principal was * sent, then null is returned. */ @Override public Principal getLocalPrincipal() { - if ((cipherSuite.keyExchange == K_KRB5) || - (cipherSuite.keyExchange == K_KRB5_EXPORT)) { - // Eliminate dependency on KerberosPrincipal + if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { return (localPrincipal == null ? null : localPrincipal); } return (localCerts == null ? null : diff --git a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java index 12bb20693ca..deea7949d9e 100644 --- a/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java +++ b/jdk/src/java.base/share/classes/sun/security/ssl/ServerHandshaker.java @@ -38,8 +38,6 @@ import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.*; -import javax.security.auth.Subject; - import sun.security.util.KeyUtil; import sun.security.action.GetPropertyAction; import sun.security.ssl.HandshakeMessage.*; @@ -237,18 +235,6 @@ final class ServerHandshaker extends Handshaker { handshakeState.update(pms, resumingSession); preMasterSecret = this.clientKeyExchange(pms); break; - case K_KRB5: - case K_KRB5_EXPORT: - KerberosClientKeyExchange kke = - new KerberosClientKeyExchange(protocolVersion, - clientRequestedVersion, - sslContext.getSecureRandom(), - input, - this.getAccSE(), - serviceCreds); - handshakeState.update(kke, resumingSession); - preMasterSecret = this.clientKeyExchange(kke); - break; case K_DHE_RSA: case K_DHE_DSS: case K_DH_ANON: @@ -273,8 +259,24 @@ final class ServerHandshaker extends Handshaker { preMasterSecret = this.clientKeyExchange(ecdhcke); break; default: - throw new SSLProtocolException - ("Unrecognized key exchange: " + keyExchange); + ClientKeyExchangeService p = + ClientKeyExchangeService.find(keyExchange.name); + if (p == null) { + throw new SSLProtocolException + ("Unrecognized key exchange: " + keyExchange); + } + byte[] encodedTicket = input.getBytes16(); + input.getBytes16(); + byte[] secret = input.getBytes16(); + ClientKeyExchange cke = p.createServerExchange(protocolVersion, + clientRequestedVersion, + sslContext.getSecureRandom(), + encodedTicket, + secret, + this.getAccSE(), serviceCreds); + handshakeState.update(cke, resumingSession); + preMasterSecret = this.clientKeyExchange(cke); + break; } // @@ -624,47 +626,21 @@ final class ServerHandshaker extends Handshaker { // validate subject identity if (resumingSession) { CipherSuite suite = previous.getSuite(); - if (suite.keyExchange == K_KRB5 || - suite.keyExchange == K_KRB5_EXPORT) { + ClientKeyExchangeService p = + ClientKeyExchangeService.find(suite.keyExchange.name); + if (p != null) { Principal localPrincipal = previous.getLocalPrincipal(); - Subject subject = null; - try { - subject = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - @Override - public Subject run() throws Exception { - return - Krb5Helper.getServerSubject(getAccSE()); - }}); - } catch (PrivilegedActionException e) { - subject = null; - if (debug != null && Debug.isOn("session")) { - System.out.println("Attempt to obtain" + - " subject failed!"); - } - } - - if (subject != null) { - // Eliminate dependency on KerberosPrincipal - if (Krb5Helper.isRelated(subject, localPrincipal)) { - if (debug != null && Debug.isOn("session")) - System.out.println("Subject can" + - " provide creds for princ"); - } else { - resumingSession = false; - if (debug != null && Debug.isOn("session")) - System.out.println("Subject cannot" + - " provide creds for princ"); - } + if (p.isRelated( + false, getAccSE(), localPrincipal)) { + if (debug != null && Debug.isOn("session")) + System.out.println("Subject can" + + " provide creds for princ"); } else { resumingSession = false; if (debug != null && Debug.isOn("session")) - System.out.println("Kerberos credentials are" + - " not present in the current Subject;" + - " check if " + - " javax.security.auth.useSubjectAsCreds" + - " system property has been set to false"); + System.out.println("Subject cannot" + + " provide creds for princ"); } } } @@ -871,9 +847,8 @@ final class ServerHandshaker extends Handshaker { * defined in the protocol spec are explicitly stated to require * using RSA certificates. */ - if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) { - // Server certificates are omitted for Kerberos ciphers - + if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) { + // No external key exchange provider needs a cert now. } else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) { if (certs == null) { throw new RuntimeException("no certificates"); @@ -915,9 +890,7 @@ final class ServerHandshaker extends Handshaker { ServerKeyExchange m3; switch (keyExchange) { case K_RSA: - case K_KRB5: - case K_KRB5_EXPORT: - // no server key exchange for RSA or KRB5 ciphersuites + // no server key exchange for RSA ciphersuites m3 = null; break; case K_RSA_EXPORT: @@ -980,6 +953,13 @@ final class ServerHandshaker extends Handshaker { m3 = null; break; default: + ClientKeyExchangeService p = + ClientKeyExchangeService.find(keyExchange.name); + if (p != null) { + // No external key exchange provider needs a cert now. + m3 = null; + break; + } throw new RuntimeException("internal error: " + keyExchange); } if (m3 != null) { @@ -1000,10 +980,10 @@ final class ServerHandshaker extends Handshaker { // Needed only if server requires client to authenticate self. // Illegal for anonymous flavors, so we need to check that. // - // CertificateRequest is omitted for Kerberos ciphers + // No external key exchange provider needs a cert now. if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE && keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON && - keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) { + ClientKeyExchangeService.find(keyExchange.name) == null) { CertificateRequest m4; X509Certificate caCerts[]; @@ -1313,13 +1293,6 @@ final class ServerHandshaker extends Handshaker { } setupStaticECDHKeys(); break; - case K_KRB5: - case K_KRB5_EXPORT: - // need Kerberos Key - if (!setupKerberosKeys()) { - return false; - } - break; case K_DH_ANON: // no certs needed for anonymous setupEphemeralDHKeys(suite.exportable, null); @@ -1331,8 +1304,26 @@ final class ServerHandshaker extends Handshaker { } break; default: - // internal error, unknown key exchange - throw new RuntimeException("Unrecognized cipherSuite: " + suite); + ClientKeyExchangeService p = + ClientKeyExchangeService.find(keyExchange.name); + if (p == null) { + // internal error, unknown key exchange + throw new RuntimeException("Unrecognized cipherSuite: " + suite); + } + // need service creds + if (serviceCreds == null) { + AccessControlContext acc = getAccSE(); + serviceCreds = p.getServiceCreds(acc); + if (serviceCreds != null) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Using serviceCreds"); + } + } + if (serviceCreds == null) { + return false; + } + } + break; } setCipherSuite(suite); @@ -1522,73 +1513,10 @@ final class ServerHandshaker extends Handshaker { return true; } - /** - * Retrieve the Kerberos key for the specified server principal - * from the JAAS configuration file. - * - * @return true if successful, false if not available or invalid - */ - private boolean setupKerberosKeys() { - if (serviceCreds != null) { - return true; - } - try { - final AccessControlContext acc = getAccSE(); - serviceCreds = AccessController.doPrivileged( - // Eliminate dependency on KerberosKey - new PrivilegedExceptionAction() { - @Override - public Object run() throws Exception { - // get kerberos key for the default principal - return Krb5Helper.getServiceCreds(acc); - }}); - - // check permission to access and use the secret key of the - // Kerberized "host" service - if (serviceCreds != null) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Using Kerberos creds"); - } - String serverPrincipal = - Krb5Helper.getServerPrincipalName(serviceCreds); - if (serverPrincipal != null) { - // When service is bound, we check ASAP. Otherwise, - // will check after client request is received - // in Kerberos ClientKeyExchange - SecurityManager sm = System.getSecurityManager(); - try { - if (sm != null) { - // Eliminate dependency on ServicePermission - sm.checkPermission(Krb5Helper.getServicePermission( - serverPrincipal, "accept"), acc); - } - } catch (SecurityException se) { - serviceCreds = null; - // Do not destroy keys. Will affect Subject - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Permission to access Kerberos" - + " secret key denied"); - } - return false; - } - } - } - return serviceCreds != null; - } catch (PrivilegedActionException e) { - // Likely exception here is LoginExceptin - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Attempt to obtain Kerberos key failed: " - + e.toString()); - } - return false; - } - } - /* - * For Kerberos ciphers, the premaster secret is encrypted using - * the session key. See RFC 2712. + * Returns premaster secret for external key exchange services. */ - private SecretKey clientKeyExchange(KerberosClientKeyExchange mesg) + private SecretKey clientKeyExchange(ClientKeyExchange mesg) throws IOException { if (debug != null && Debug.isOn("handshake")) { @@ -1599,8 +1527,7 @@ final class ServerHandshaker extends Handshaker { session.setPeerPrincipal(mesg.getPeerPrincipal()); session.setLocalPrincipal(mesg.getLocalPrincipal()); - byte[] b = mesg.getUnencryptedPreMasterSecret(); - return new SecretKeySpec(b, "TlsPremasterSecret"); + return mesg.clientKeyExchange(); } /* @@ -1705,7 +1632,7 @@ final class ServerHandshaker extends Handshaker { */ if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) { // get X500Principal of the end-entity certificate for X509-based - // ciphersuites, or Kerberos principal for Kerberos ciphersuites + // ciphersuites, or Kerberos principal for Kerberos ciphersuites, etc session.getPeerPrincipal(); } diff --git a/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java b/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java index 88d98573951..02d65d1deda 100644 --- a/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java +++ b/jdk/src/java.base/share/classes/sun/security/util/HostnameChecker.java @@ -35,7 +35,7 @@ import java.security.cert.*; import javax.security.auth.x500.X500Principal; -import sun.security.ssl.Krb5Helper; +import sun.security.ssl.ClientKeyExchangeService; import sun.security.x509.X500Name; import sun.net.util.IPAddressUtil; @@ -108,7 +108,12 @@ public class HostnameChecker { * Return the Server name from Kerberos principal. */ public static String getServerName(Principal principal) { - return Krb5Helper.getPrincipalHostName(principal); + ClientKeyExchangeService p = + ClientKeyExchangeService.find("KRB5"); + if (p == null) { + throw new AssertionError("Kerberos should have been available"); + } + return p.getServiceHostName(principal); } /** diff --git a/jdk/src/java.security.jgss/share/classes/META-INF/services/sun.security.ssl.ClientKeyExchangeService b/jdk/src/java.security.jgss/share/classes/META-INF/services/sun.security.ssl.ClientKeyExchangeService new file mode 100644 index 00000000000..883cba32174 --- /dev/null +++ b/jdk/src/java.security.jgss/share/classes/META-INF/services/sun.security.ssl.ClientKeyExchangeService @@ -0,0 +1,2 @@ +sun.security.krb5.internal.ssl.Krb5KeyExchangeService + diff --git a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java similarity index 85% rename from jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java rename to jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java index 84c96a35b3f..c1e89653a91 100644 --- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/KerberosPreMasterSecret.java @@ -23,7 +23,7 @@ * questions. */ -package sun.security.ssl.krb5; +package sun.security.krb5.internal.ssl; import java.io.*; import java.security.*; @@ -68,13 +68,13 @@ final class KerberosPreMasterSecret { * @param sessionKey Kerberos session key for encrypting premaster secret */ KerberosPreMasterSecret(ProtocolVersion protocolVersion, - SecureRandom generator, EncryptionKey sessionKey) throws IOException { + SecureRandom generator, EncryptionKey sessionKey) throws IOException { if (sessionKey.getEType() == - EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { + EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { throw new IOException( - "session keys with des3-cbc-hmac-sha1-kd encryption type " + - "are not supported for TLS Kerberos cipher suites"); + "session keys with des3-cbc-hmac-sha1-kd encryption type " + + "are not supported for TLS Kerberos cipher suites"); } this.protocolVersion = protocolVersion; @@ -83,12 +83,12 @@ final class KerberosPreMasterSecret { // Encrypt premaster secret try { EncryptedData eData = new EncryptedData(sessionKey, preMaster, - KeyUsage.KU_UNKNOWN); + KeyUsage.KU_UNKNOWN); encrypted = eData.getBytes(); // not ASN.1 encoded. } catch (KrbException e) { throw (SSLKeyException)new SSLKeyException - ("Kerberos premaster secret error").initCause(e); + ("Kerberos premaster secret error").initCause(e); } } @@ -107,38 +107,35 @@ final class KerberosPreMasterSecret { * @param sessionKey Kerberos session key to be used for decryption */ KerberosPreMasterSecret(ProtocolVersion currentVersion, - ProtocolVersion clientVersion, - SecureRandom generator, HandshakeInStream input, - EncryptionKey sessionKey) throws IOException { + ProtocolVersion clientVersion, + SecureRandom generator, byte[] encrypted, + EncryptionKey sessionKey) throws IOException { - // Extract encrypted premaster secret from message - encrypted = input.getBytes16(); - - if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { + if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { if (encrypted != null) { Debug.println(System.out, - "encrypted premaster secret", encrypted); + "encrypted premaster secret", encrypted); } - } + } if (sessionKey.getEType() == - EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { + EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) { throw new IOException( - "session keys with des3-cbc-hmac-sha1-kd encryption type " + - "are not supported for TLS Kerberos cipher suites"); + "session keys with des3-cbc-hmac-sha1-kd encryption type " + + "are not supported for TLS Kerberos cipher suites"); } // Decrypt premaster secret try { EncryptedData data = new EncryptedData(sessionKey.getEType(), - null /* optional kvno */, encrypted); + null /* optional kvno */, encrypted); byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN); if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { - if (encrypted != null) { - Debug.println(System.out, - "decrypted premaster secret", temp); - } + if (encrypted != null) { + Debug.println(System.out, + "decrypted premaster secret", temp); + } } // Remove padding bytes after decryption. Only DES and DES3 have @@ -162,9 +159,9 @@ final class KerberosPreMasterSecret { preMaster = temp; protocolVersion = ProtocolVersion.valueOf(preMaster[0], - preMaster[1]); + preMaster[1]); if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { - System.out.println("Kerberos PreMasterSecret version: " + System.out.println("Kerberos PreMasterSecret version: " + protocolVersion); } } catch (Exception e) { @@ -202,11 +199,11 @@ final class KerberosPreMasterSecret { * that will prevent an attacking client from differentiating * different kinds of decrypted ClientKeyExchange bogosities. */ - if ((preMaster == null) || (preMaster.length != 48) + if ((preMaster == null) || (preMaster.length != 48) || versionMismatch) { if (HandshakeMessage.debug != null && Debug.isOn("handshake")) { System.out.println("Kerberos PreMasterSecret error, " - + "generating random secret"); + + "generating random secret"); if (preMaster != null) { Debug.println(System.out, "Invalid secret", preMaster); } @@ -243,14 +240,14 @@ final class KerberosPreMasterSecret { * @param generator random number generator to use for generating secret. */ KerberosPreMasterSecret(ProtocolVersion protocolVersion, - SecureRandom generator) { + SecureRandom generator) { this.protocolVersion = protocolVersion; preMaster = generatePreMaster(generator, protocolVersion); } private static byte[] generatePreMaster(SecureRandom rand, - ProtocolVersion ver) { + ProtocolVersion ver) { byte[] pm = new byte[48]; rand.nextBytes(pm); diff --git a/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java new file mode 100644 index 00000000000..ee7cffae17c --- /dev/null +++ b/jdk/src/java.security.jgss/share/classes/sun/security/krb5/internal/ssl/Krb5KeyExchangeService.java @@ -0,0 +1,557 @@ +/* + * Copyright (c) 2015, 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. 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.krb5.internal.ssl; + +import sun.security.ssl.ClientKeyExchange; +import sun.security.ssl.Debug; +import sun.security.ssl.ClientKeyExchangeService; +import sun.security.ssl.HandshakeOutStream; + +import sun.security.jgss.GSSCaller; +import sun.security.jgss.krb5.Krb5Util; +import sun.security.jgss.krb5.ServiceCreds; +import sun.security.krb5.EncryptedData; +import sun.security.krb5.EncryptionKey; +import sun.security.krb5.KrbException; +import sun.security.krb5.PrincipalName; +import sun.security.krb5.internal.EncTicketPart; +import sun.security.krb5.internal.Ticket; +import sun.security.krb5.internal.crypto.KeyUsage; +import sun.security.ssl.ProtocolVersion; + +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosKey; +import javax.security.auth.kerberos.KerberosPrincipal; +import javax.security.auth.kerberos.KerberosTicket; +import javax.security.auth.kerberos.KeyTab; +import javax.security.auth.kerberos.ServicePermission; +import java.io.IOException; +import java.io.PrintStream; +import java.net.InetAddress; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.SecureRandom; +import java.util.Set; + +/** + * The provider for TLS_KRB_ cipher suites. + * + * @since 1.9 + */ +public class Krb5KeyExchangeService implements ClientKeyExchangeService { + + public static final Debug debug = Debug.getInstance("ssl"); + + @Override + public String[] supported() { + return new String[] { "KRB5", "KRB5_EXPORT" }; + } + + @Override + public Object getServiceCreds(AccessControlContext acc) { + try { + ServiceCreds serviceCreds = AccessController.doPrivileged( + (PrivilegedExceptionAction) + () -> Krb5Util.getServiceCreds( + GSSCaller.CALLER_SSL_SERVER, null, acc)); + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Using Kerberos creds"); + } + String serverPrincipal = serviceCreds.getName(); + if (serverPrincipal != null) { + // When service is bound, we check ASAP. Otherwise, + // will check after client request is received + // in in Kerberos ClientKeyExchange + SecurityManager sm = System.getSecurityManager(); + try { + if (sm != null) { + // Eliminate dependency on ServicePermission + sm.checkPermission(new ServicePermission( + serverPrincipal, "accept"), acc); + } + } catch (SecurityException se) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Permission to access Kerberos" + + " secret key denied"); + } + return null; + } + } + return serviceCreds; + } catch (PrivilegedActionException e) { + // Likely exception here is LoginException + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Attempt to obtain Kerberos key failed: " + + e.toString()); + } + return null; + } + } + + @Override + public String getServiceHostName(Principal principal) { + if (principal == null) { + return null; + } + String hostName = null; + try { + PrincipalName princName = + new PrincipalName(principal.getName(), + PrincipalName.KRB_NT_SRV_HST); + String[] nameParts = princName.getNameStrings(); + if (nameParts.length >= 2) { + hostName = nameParts[1]; + } + } catch (Exception e) { + // ignore + } + return hostName; + } + + + @Override + public boolean isRelated(boolean isClient, + AccessControlContext acc, Principal p) { + + if (p == null) return false; + try { + Subject subject = AccessController.doPrivileged( + (PrivilegedExceptionAction) + () -> Krb5Util.getSubject( + isClient ? GSSCaller.CALLER_SSL_CLIENT + : GSSCaller.CALLER_SSL_SERVER, + acc)); + if (subject == null) { + if (debug != null && Debug.isOn("session")) { + System.out.println("Kerberos credentials are" + + " not present in the current Subject;" + + " check if " + + " javax.security.auth.useSubjectAsCreds" + + " system property has been set to false"); + } + return false; + } + Set principals = + subject.getPrincipals(Principal.class); + if (principals.contains(p)) { + // bound to this principal + return true; + } else { + if (isClient) { + return false; + } else { + for (KeyTab pc : subject.getPrivateCredentials(KeyTab.class)) { + if (!pc.isBound()) { + return true; + } + } + return false; + } + } + } catch (PrivilegedActionException pae) { + if (debug != null && Debug.isOn("session")) { + System.out.println("Attempt to obtain" + + " subject failed! " + pae); + } + return false; + } + + } + + public ClientKeyExchange createClientExchange( + String serverName, AccessControlContext acc, + ProtocolVersion protocolVerson, SecureRandom rand) throws IOException { + return new ExchangerImpl(serverName, acc, protocolVerson, rand); + } + + public ClientKeyExchange createServerExchange( + ProtocolVersion protocolVersion, ProtocolVersion clientVersion, + SecureRandom rand, byte[] encodedTicket, byte[] encrypted, + AccessControlContext acc, Object serviceCreds) throws IOException { + return new ExchangerImpl(protocolVersion, clientVersion, rand, + encodedTicket, encrypted, acc, serviceCreds); + } + + static class ExchangerImpl extends ClientKeyExchange { + + final private KerberosPreMasterSecret preMaster; + final private byte[] encodedTicket; + final private KerberosPrincipal peerPrincipal; + final private KerberosPrincipal localPrincipal; + + @Override + public int messageLength() { + return encodedTicket.length + preMaster.getEncrypted().length + 6; + } + + @Override + public void send(HandshakeOutStream s) throws IOException { + s.putBytes16(encodedTicket); + s.putBytes16(null); + s.putBytes16(preMaster.getEncrypted()); + } + + @Override + public void print(PrintStream s) throws IOException { + s.println("*** ClientKeyExchange, Kerberos"); + + if (debug != null && Debug.isOn("verbose")) { + Debug.println(s, "Kerberos service ticket", encodedTicket); + Debug.println(s, "Random Secret", preMaster.getUnencrypted()); + Debug.println(s, "Encrypted random Secret", preMaster.getEncrypted()); + } + } + + ExchangerImpl(String serverName, AccessControlContext acc, + ProtocolVersion protocolVersion, SecureRandom rand) throws IOException { + + // Get service ticket + KerberosTicket ticket = getServiceTicket(serverName, acc); + encodedTicket = ticket.getEncoded(); + + // Record the Kerberos principals + peerPrincipal = ticket.getServer(); + localPrincipal = ticket.getClient(); + + // Optional authenticator, encrypted using session key, + // currently ignored + + // Generate premaster secret and encrypt it using session key + EncryptionKey sessionKey = new EncryptionKey( + ticket.getSessionKeyType(), + ticket.getSessionKey().getEncoded()); + + preMaster = new KerberosPreMasterSecret(protocolVersion, + rand, sessionKey); + } + + ExchangerImpl( + ProtocolVersion protocolVersion, ProtocolVersion clientVersion, SecureRandom rand, + byte[] encodedTicket, byte[] encrypted, + AccessControlContext acc, Object serviceCreds) throws IOException { + + // Read ticket + this.encodedTicket = encodedTicket; + + if (debug != null && Debug.isOn("verbose")) { + Debug.println(System.out, + "encoded Kerberos service ticket", encodedTicket); + } + + EncryptionKey sessionKey = null; + KerberosPrincipal tmpPeer = null; + KerberosPrincipal tmpLocal = null; + + try { + Ticket t = new Ticket(encodedTicket); + + EncryptedData encPart = t.encPart; + PrincipalName ticketSname = t.sname; + + final ServiceCreds creds = (ServiceCreds)serviceCreds; + final KerberosPrincipal princ = + new KerberosPrincipal(ticketSname.toString()); + + // For bound service, permission already checked at setup + if (creds.getName() == null) { + SecurityManager sm = System.getSecurityManager(); + try { + if (sm != null) { + // Eliminate dependency on ServicePermission + sm.checkPermission(new ServicePermission( + ticketSname.toString(), "accept"), acc); + } + } catch (SecurityException se) { + serviceCreds = null; + // Do not destroy keys. Will affect Subject + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Permission to access Kerberos" + + " secret key denied"); + se.printStackTrace(System.out); + } + throw new IOException("Kerberos service not allowedy"); + } + } + KerberosKey[] serverKeys = AccessController.doPrivileged( + new PrivilegedAction() { + @Override + public KerberosKey[] run() { + return creds.getKKeys(princ); + } + }); + if (serverKeys.length == 0) { + throw new IOException("Found no key for " + princ + + (creds.getName() == null ? "" : + (", this keytab is for " + creds.getName() + " only"))); + } + + /* + * permission to access and use the secret key of the Kerberized + * "host" service is done in ServerHandshaker.getKerberosKeys() + * to ensure server has the permission to use the secret key + * before promising the client + */ + + // See if we have the right key to decrypt the ticket to get + // the session key. + int encPartKeyType = encPart.getEType(); + Integer encPartKeyVersion = encPart.getKeyVersionNumber(); + KerberosKey dkey = null; + try { + dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys); + } catch (KrbException ke) { // a kvno mismatch + throw new IOException( + "Cannot find key matching version number", ke); + } + if (dkey == null) { + // %%% Should print string repr of etype + throw new IOException("Cannot find key of appropriate type" + + " to decrypt ticket - need etype " + encPartKeyType); + } + + EncryptionKey secretKey = new EncryptionKey( + encPartKeyType, + dkey.getEncoded()); + + // Decrypt encPart using server's secret key + byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET); + + // Reset data stream after decryption, remove redundant bytes + byte[] temp = encPart.reset(bytes); + EncTicketPart encTicketPart = new EncTicketPart(temp); + + // Record the Kerberos Principals + tmpPeer = new KerberosPrincipal(encTicketPart.cname.getName()); + tmpLocal = new KerberosPrincipal(ticketSname.getName()); + + sessionKey = encTicketPart.key; + + if (debug != null && Debug.isOn("handshake")) { + System.out.println("server principal: " + ticketSname); + System.out.println("cname: " + encTicketPart.cname.toString()); + } + } catch (IOException e) { + throw e; + } catch (Exception e) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println("KerberosWrapper error getting session key," + + " generating random secret (" + e.getMessage() + ")"); + } + sessionKey = null; + } + + //input.getBytes16(); // XXX Read and ignore authenticator + + if (sessionKey != null) { + preMaster = new KerberosPreMasterSecret(protocolVersion, + clientVersion, rand, encrypted, sessionKey); + } else { + // Generate bogus premaster secret + preMaster = new KerberosPreMasterSecret(clientVersion, rand); + } + + peerPrincipal = tmpPeer; + localPrincipal = tmpLocal; + } + + // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context + private static KerberosTicket getServiceTicket(String serverName, + final AccessControlContext acc) throws IOException { + + if ("localhost".equals(serverName) || + "localhost.localdomain".equals(serverName)) { + + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Get the local hostname"); + } + String localHost = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch (java.net.UnknownHostException e) { + if (debug != null && Debug.isOn("handshake")) { + System.out.println("Warning," + + " cannot get the local hostname: " + + e.getMessage()); + } + return null; + } + } + }); + if (localHost != null) { + serverName = localHost; + } + } + + // Resolve serverName (possibly in IP addr form) to Kerberos principal + // name for service with hostname + String serviceName = "host/" + serverName; + PrincipalName principal; + try { + principal = new PrincipalName(serviceName, + PrincipalName.KRB_NT_SRV_HST); + } catch (SecurityException se) { + throw se; + } catch (Exception e) { + IOException ioe = new IOException("Invalid service principal" + + " name: " + serviceName); + ioe.initCause(e); + throw ioe; + } + String realm = principal.getRealmAsString(); + + final String serverPrincipal = principal.toString(); + final String tgsPrincipal = "krbtgt/" + realm + "@" + realm; + final String clientPrincipal = null; // use default + + + // check permission to obtain a service ticket to initiate a + // context with the "host" service + SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new ServicePermission(serverPrincipal, + "initiate"), acc); + } + + try { + KerberosTicket ticket = AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public KerberosTicket run() throws Exception { + return Krb5Util.getTicketFromSubjectAndTgs( + GSSCaller.CALLER_SSL_CLIENT, + clientPrincipal, serverPrincipal, + tgsPrincipal, acc); + }}); + + if (ticket == null) { + throw new IOException("Failed to find any kerberos service" + + " ticket for " + serverPrincipal); + } + return ticket; + } catch (PrivilegedActionException e) { + IOException ioe = new IOException( + "Attempt to obtain kerberos service ticket for " + + serverPrincipal + " failed!"); + ioe.initCause(e); + throw ioe; + } + } + + @Override + public SecretKey clientKeyExchange() { + byte[] secretBytes = preMaster.getUnencrypted(); + return new SecretKeySpec(secretBytes, "TlsPremasterSecret"); + } + + @Override + public Principal getPeerPrincipal() { + return peerPrincipal; + } + + @Override + public Principal getLocalPrincipal() { + return localPrincipal; + } + + /** + * Determines if a kvno matches another kvno. Used in the method + * findKey(etype, version, keys). Always returns true if either input + * is null or zero, in case any side does not have kvno info available. + * + * Note: zero is included because N/A is not a legal value for kvno + * in javax.security.auth.kerberos.KerberosKey. Therefore, the info + * that the kvno is N/A might be lost when converting between + * EncryptionKey and KerberosKey. + */ + private static boolean versionMatches(Integer v1, int v2) { + if (v1 == null || v1 == 0 || v2 == 0) { + return true; + } + return v1.equals(v2); + } + + private static KerberosKey findKey(int etype, Integer version, + KerberosKey[] keys) throws KrbException { + int ktype; + boolean etypeFound = false; + + // When no matched kvno is found, returns tke key of the same + // etype with the highest kvno + int kvno_found = 0; + KerberosKey key_found = null; + + for (int i = 0; i < keys.length; i++) { + ktype = keys[i].getKeyType(); + if (etype == ktype) { + int kv = keys[i].getVersionNumber(); + etypeFound = true; + if (versionMatches(version, kv)) { + return keys[i]; + } else if (kv > kvno_found) { + key_found = keys[i]; + kvno_found = kv; + } + } + } + // Key not found. + // %%% kludge to allow DES keys to be used for diff etypes + if ((etype == EncryptedData.ETYPE_DES_CBC_CRC || + etype == EncryptedData.ETYPE_DES_CBC_MD5)) { + for (int i = 0; i < keys.length; i++) { + ktype = keys[i].getKeyType(); + if (ktype == EncryptedData.ETYPE_DES_CBC_CRC || + ktype == EncryptedData.ETYPE_DES_CBC_MD5) { + int kv = keys[i].getVersionNumber(); + etypeFound = true; + if (versionMatches(version, kv)) { + return new KerberosKey(keys[i].getPrincipal(), + keys[i].getEncoded(), + etype, + kv); + } else if (kv > kvno_found) { + key_found = new KerberosKey(keys[i].getPrincipal(), + keys[i].getEncoded(), + etype, + kv); + kvno_found = kv; + } + } + } + } + if (etypeFound) { + return key_found; + } + return null; + } + } +} diff --git a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java b/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java deleted file mode 100644 index 1fb770871d8..00000000000 --- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java +++ /dev/null @@ -1,463 +0,0 @@ -/* - * Copyright (c) 2003, 2013, 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. 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.ssl.krb5; - -import java.io.IOException; -import java.io.PrintStream; -import java.security.AccessController; -import java.security.AccessControlContext; -import java.security.PrivilegedExceptionAction; -import java.security.PrivilegedActionException; -import java.security.SecureRandom; -import java.net.InetAddress; -import java.security.PrivilegedAction; - -import javax.security.auth.kerberos.KerberosTicket; -import javax.security.auth.kerberos.KerberosKey; -import javax.security.auth.kerberos.KerberosPrincipal; -import javax.security.auth.kerberos.ServicePermission; -import sun.security.jgss.GSSCaller; - -import sun.security.krb5.EncryptionKey; -import sun.security.krb5.EncryptedData; -import sun.security.krb5.PrincipalName; -import sun.security.krb5.internal.Ticket; -import sun.security.krb5.internal.EncTicketPart; -import sun.security.krb5.internal.crypto.KeyUsage; - -import sun.security.jgss.krb5.Krb5Util; -import sun.security.jgss.krb5.ServiceCreds; -import sun.security.krb5.KrbException; -import sun.security.krb5.internal.Krb5; - -import sun.security.ssl.Debug; -import sun.security.ssl.HandshakeInStream; -import sun.security.ssl.HandshakeOutStream; -import sun.security.ssl.Krb5Helper; -import sun.security.ssl.ProtocolVersion; - -/** - * This is Kerberos option in the client key exchange message - * (CLIENT -> SERVER). It holds the Kerberos ticket and the encrypted - * premaster secret encrypted with the session key sealed in the ticket. - * From RFC 2712: - * struct - * { - * opaque Ticket; - * opaque authenticator; // optional - * opaque EncryptedPreMasterSecret; // encrypted with the session key - * // which is sealed in the ticket - * } KerberosWrapper; - * - * - * Ticket and authenticator are encrypted as per RFC 1510 (in ASN.1) - * Encrypted pre-master secret has the same structure as it does for RSA - * except for Kerberos, the encryption key is the session key instead of - * the RSA public key. - * - * XXX authenticator currently ignored - * - */ -public final class KerberosClientKeyExchangeImpl - extends sun.security.ssl.KerberosClientKeyExchange { - - private KerberosPreMasterSecret preMaster; - private byte[] encodedTicket; - private KerberosPrincipal peerPrincipal; - private KerberosPrincipal localPrincipal; - - public KerberosClientKeyExchangeImpl() { - } - - /** - * Creates an instance of KerberosClientKeyExchange consisting of the - * Kerberos service ticket, authenticator and encrypted premaster secret. - * Called by client handshaker. - * - * @param serverName name of server with which to do handshake; - * this is used to get the Kerberos service ticket - * @param protocolVersion Maximum version supported by client (i.e, - * version it requested in client hello) - * @param rand random number generator to use for generating pre-master - * secret - */ - @Override - public void init(String serverName, - AccessControlContext acc, ProtocolVersion protocolVersion, - SecureRandom rand) throws IOException { - - // Get service ticket - KerberosTicket ticket = getServiceTicket(serverName, acc); - encodedTicket = ticket.getEncoded(); - - // Record the Kerberos principals - peerPrincipal = ticket.getServer(); - localPrincipal = ticket.getClient(); - - // Optional authenticator, encrypted using session key, - // currently ignored - - // Generate premaster secret and encrypt it using session key - EncryptionKey sessionKey = new EncryptionKey( - ticket.getSessionKeyType(), - ticket.getSessionKey().getEncoded()); - - preMaster = new KerberosPreMasterSecret(protocolVersion, - rand, sessionKey); - } - - /** - * Creates an instance of KerberosClientKeyExchange from its ASN.1 encoding. - * Used by ServerHandshaker to verify and obtain premaster secret. - * - * @param protocolVersion current protocol version - * @param clientVersion version requested by client in its ClientHello; - * used by premaster secret version check - * @param rand random number generator used for generating random - * premaster secret if ticket and/or premaster verification fails - * @param input inputstream from which to get ASN.1-encoded KerberosWrapper - * @param acc the AccessControlContext of the handshaker - * @param serviceCreds server's creds - */ - @Override - public void init(ProtocolVersion protocolVersion, - ProtocolVersion clientVersion, - SecureRandom rand, HandshakeInStream input, AccessControlContext acc, Object serviceCreds) - throws IOException { - - // Read ticket - encodedTicket = input.getBytes16(); - - if (debug != null && Debug.isOn("verbose")) { - Debug.println(System.out, - "encoded Kerberos service ticket", encodedTicket); - } - - EncryptionKey sessionKey = null; - - try { - Ticket t = new Ticket(encodedTicket); - - EncryptedData encPart = t.encPart; - PrincipalName ticketSname = t.sname; - - final ServiceCreds creds = (ServiceCreds)serviceCreds; - final KerberosPrincipal princ = - new KerberosPrincipal(ticketSname.toString()); - - // For bound service, permission already checked at setup - if (creds.getName() == null) { - SecurityManager sm = System.getSecurityManager(); - try { - if (sm != null) { - // Eliminate dependency on ServicePermission - sm.checkPermission(Krb5Helper.getServicePermission( - ticketSname.toString(), "accept"), acc); - } - } catch (SecurityException se) { - serviceCreds = null; - // Do not destroy keys. Will affect Subject - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Permission to access Kerberos" - + " secret key denied"); - } - throw new IOException("Kerberos service not allowedy"); - } - } - KerberosKey[] serverKeys = AccessController.doPrivileged( - new PrivilegedAction() { - @Override - public KerberosKey[] run() { - return creds.getKKeys(princ); - } - }); - if (serverKeys.length == 0) { - throw new IOException("Found no key for " + princ + - (creds.getName() == null ? "" : - (", this keytab is for " + creds.getName() + " only"))); - } - - /* - * permission to access and use the secret key of the Kerberized - * "host" service is done in ServerHandshaker.getKerberosKeys() - * to ensure server has the permission to use the secret key - * before promising the client - */ - - // See if we have the right key to decrypt the ticket to get - // the session key. - int encPartKeyType = encPart.getEType(); - Integer encPartKeyVersion = encPart.getKeyVersionNumber(); - KerberosKey dkey = null; - try { - dkey = findKey(encPartKeyType, encPartKeyVersion, serverKeys); - } catch (KrbException ke) { // a kvno mismatch - throw new IOException( - "Cannot find key matching version number", ke); - } - if (dkey == null) { - // %%% Should print string repr of etype - throw new IOException("Cannot find key of appropriate type" + - " to decrypt ticket - need etype " + encPartKeyType); - } - - EncryptionKey secretKey = new EncryptionKey( - encPartKeyType, - dkey.getEncoded()); - - // Decrypt encPart using server's secret key - byte[] bytes = encPart.decrypt(secretKey, KeyUsage.KU_TICKET); - - // Reset data stream after decryption, remove redundant bytes - byte[] temp = encPart.reset(bytes); - EncTicketPart encTicketPart = new EncTicketPart(temp); - - // Record the Kerberos Principals - peerPrincipal = - new KerberosPrincipal(encTicketPart.cname.getName()); - localPrincipal = new KerberosPrincipal(ticketSname.getName()); - - sessionKey = encTicketPart.key; - - if (debug != null && Debug.isOn("handshake")) { - System.out.println("server principal: " + ticketSname); - System.out.println("cname: " + encTicketPart.cname.toString()); - } - } catch (IOException e) { - throw e; - } catch (Exception e) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("KerberosWrapper error getting session key," - + " generating random secret (" + e.getMessage() + ")"); - } - sessionKey = null; - } - - input.getBytes16(); // XXX Read and ignore authenticator - - if (sessionKey != null) { - preMaster = new KerberosPreMasterSecret(protocolVersion, - clientVersion, rand, input, sessionKey); - } else { - // Generate bogus premaster secret - preMaster = new KerberosPreMasterSecret(clientVersion, rand); - } - } - - @Override - public int messageLength() { - return (6 + encodedTicket.length + preMaster.getEncrypted().length); - } - - @Override - public void send(HandshakeOutStream s) throws IOException { - s.putBytes16(encodedTicket); - s.putBytes16(null); // XXX no authenticator - s.putBytes16(preMaster.getEncrypted()); - } - - @Override - public void print(PrintStream s) throws IOException { - s.println("*** ClientKeyExchange, Kerberos"); - - if (debug != null && Debug.isOn("verbose")) { - Debug.println(s, "Kerberos service ticket", encodedTicket); - Debug.println(s, "Random Secret", preMaster.getUnencrypted()); - Debug.println(s, "Encrypted random Secret", - preMaster.getEncrypted()); - } - } - - // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context - private static KerberosTicket getServiceTicket(String serverName, - final AccessControlContext acc) throws IOException { - - if ("localhost".equals(serverName) || - "localhost.localdomain".equals(serverName)) { - - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Get the local hostname"); - } - String localHost = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public String run() { - try { - return InetAddress.getLocalHost().getHostName(); - } catch (java.net.UnknownHostException e) { - if (debug != null && Debug.isOn("handshake")) { - System.out.println("Warning," - + " cannot get the local hostname: " - + e.getMessage()); - } - return null; - } - } - }); - if (localHost != null) { - serverName = localHost; - } - } - - // Resolve serverName (possibly in IP addr form) to Kerberos principal - // name for service with hostname - String serviceName = "host/" + serverName; - PrincipalName principal; - try { - principal = new PrincipalName(serviceName, - PrincipalName.KRB_NT_SRV_HST); - } catch (SecurityException se) { - throw se; - } catch (Exception e) { - IOException ioe = new IOException("Invalid service principal" + - " name: " + serviceName); - ioe.initCause(e); - throw ioe; - } - String realm = principal.getRealmAsString(); - - final String serverPrincipal = principal.toString(); - final String tgsPrincipal = "krbtgt/" + realm + "@" + realm; - final String clientPrincipal = null; // use default - - - // check permission to obtain a service ticket to initiate a - // context with the "host" service - SecurityManager sm = System.getSecurityManager(); - if (sm != null) { - sm.checkPermission(new ServicePermission(serverPrincipal, - "initiate"), acc); - } - - try { - KerberosTicket ticket = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public KerberosTicket run() throws Exception { - return Krb5Util.getTicketFromSubjectAndTgs( - GSSCaller.CALLER_SSL_CLIENT, - clientPrincipal, serverPrincipal, - tgsPrincipal, acc); - }}); - - if (ticket == null) { - throw new IOException("Failed to find any kerberos service" + - " ticket for " + serverPrincipal); - } - return ticket; - } catch (PrivilegedActionException e) { - IOException ioe = new IOException( - "Attempt to obtain kerberos service ticket for " + - serverPrincipal + " failed!"); - ioe.initCause(e); - throw ioe; - } - } - - @Override - public byte[] getUnencryptedPreMasterSecret() { - return preMaster.getUnencrypted(); - } - - @Override - public KerberosPrincipal getPeerPrincipal() { - return peerPrincipal; - } - - @Override - public KerberosPrincipal getLocalPrincipal() { - return localPrincipal; - } - - /** - * Determines if a kvno matches another kvno. Used in the method - * findKey(etype, version, keys). Always returns true if either input - * is null or zero, in case any side does not have kvno info available. - * - * Note: zero is included because N/A is not a legal value for kvno - * in javax.security.auth.kerberos.KerberosKey. Therefore, the info - * that the kvno is N/A might be lost when converting between - * EncryptionKey and KerberosKey. - */ - private static boolean versionMatches(Integer v1, int v2) { - if (v1 == null || v1 == 0 || v2 == 0) { - return true; - } - return v1.equals(v2); - } - - private static KerberosKey findKey(int etype, Integer version, - KerberosKey[] keys) throws KrbException { - int ktype; - boolean etypeFound = false; - - // When no matched kvno is found, returns tke key of the same - // etype with the highest kvno - int kvno_found = 0; - KerberosKey key_found = null; - - for (int i = 0; i < keys.length; i++) { - ktype = keys[i].getKeyType(); - if (etype == ktype) { - int kv = keys[i].getVersionNumber(); - etypeFound = true; - if (versionMatches(version, kv)) { - return keys[i]; - } else if (kv > kvno_found) { - key_found = keys[i]; - kvno_found = kv; - } - } - } - // Key not found. - // %%% kludge to allow DES keys to be used for diff etypes - if ((etype == EncryptedData.ETYPE_DES_CBC_CRC || - etype == EncryptedData.ETYPE_DES_CBC_MD5)) { - for (int i = 0; i < keys.length; i++) { - ktype = keys[i].getKeyType(); - if (ktype == EncryptedData.ETYPE_DES_CBC_CRC || - ktype == EncryptedData.ETYPE_DES_CBC_MD5) { - int kv = keys[i].getVersionNumber(); - etypeFound = true; - if (versionMatches(version, kv)) { - return new KerberosKey(keys[i].getPrincipal(), - keys[i].getEncoded(), - etype, - kv); - } else if (kv > kvno_found) { - key_found = new KerberosKey(keys[i].getPrincipal(), - keys[i].getEncoded(), - etype, - kv); - kvno_found = kv; - } - } - } - } - if (etypeFound) { - return key_found; - } - return null; - } -} diff --git a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java b/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java deleted file mode 100644 index 130ba74d2f2..00000000000 --- a/jdk/src/java.security.jgss/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2009, 2013, 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. 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.ssl.krb5; - -import java.security.AccessControlContext; -import java.security.Permission; -import java.security.Principal; -import java.util.Set; -import javax.crypto.SecretKey; -import javax.security.auth.Subject; -import javax.security.auth.kerberos.KerberosKey; -import javax.security.auth.kerberos.KeyTab; -import javax.security.auth.kerberos.ServicePermission; -import javax.security.auth.login.LoginException; - -import sun.security.jgss.GSSCaller; -import sun.security.jgss.krb5.Krb5Util; -import sun.security.jgss.krb5.ServiceCreds; -import sun.security.krb5.PrincipalName; -import sun.security.ssl.Krb5Proxy; - -/** - * An implementation of Krb5Proxy that simply delegates to the appropriate - * Kerberos APIs. - */ -public class Krb5ProxyImpl implements Krb5Proxy { - - public Krb5ProxyImpl() { } - - @Override - public Subject getClientSubject(AccessControlContext acc) - throws LoginException { - return Krb5Util.getSubject(GSSCaller.CALLER_SSL_CLIENT, acc); - } - - @Override - public Subject getServerSubject(AccessControlContext acc) - throws LoginException { - return Krb5Util.getSubject(GSSCaller.CALLER_SSL_SERVER, acc); - } - - @Override - public Object getServiceCreds(AccessControlContext acc) - throws LoginException { - ServiceCreds serviceCreds = - Krb5Util.getServiceCreds(GSSCaller.CALLER_SSL_SERVER, null, acc); - return serviceCreds; - } - - @Override - public String getServerPrincipalName(Object serviceCreds) { - return ((ServiceCreds)serviceCreds).getName(); - } - - @Override - public String getPrincipalHostName(Principal principal) { - if (principal == null) { - return null; - } - String hostName = null; - try { - PrincipalName princName = - new PrincipalName(principal.getName(), - PrincipalName.KRB_NT_SRV_HST); - String[] nameParts = princName.getNameStrings(); - if (nameParts.length >= 2) { - hostName = nameParts[1]; - } - } catch (Exception e) { - // ignore - } - return hostName; - } - - - @Override - public Permission getServicePermission(String principalName, - String action) { - return new ServicePermission(principalName, action); - } - - @Override - public boolean isRelated(Subject subject, Principal princ) { - if (princ == null) return false; - Set principals = - subject.getPrincipals(Principal.class); - if (principals.contains(princ)) { - // bound to this principal - return true; - } - for (KeyTab pc: subject.getPrivateCredentials(KeyTab.class)) { - if (!pc.isBound()) { - return true; - } - } - return false; - } -} diff --git a/jdk/test/sun/security/krb5/auto/SSLwithPerms.java b/jdk/test/sun/security/krb5/auto/SSLwithPerms.java new file mode 100644 index 00000000000..9d195e350f0 --- /dev/null +++ b/jdk/test/sun/security/krb5/auto/SSLwithPerms.java @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2015, 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 8038089 + * @summary TLS optional support for Kerberos cipher suites needs to be re-examined + * @library ../../../../java/security/testlibrary/ + * @run main/othervm SSLwithPerms + */ +import java.io.*; +import javax.net.ssl.*; +import javax.security.auth.AuthPermission; +import javax.security.auth.kerberos.ServicePermission; +import java.net.SocketPermission; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.security.Principal; +import java.security.Security; +import java.security.SecurityPermission; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.ArrayList; +import java.util.Locale; +import java.util.PropertyPermission; + +import sun.security.jgss.GSSUtil; + +public class SSLwithPerms { + + static String KRB5_CONF = "krb5.conf"; + static String JAAS_CONF = "jaas.conf"; + static String REALM = "REALM"; + static String KTAB = "ktab"; + static String HOST = "host." + REALM.toLowerCase(Locale.US); + static String SERVER = "host/" + HOST; + static String USER = "user"; + static char[] PASS = "password".toCharArray(); + + public static void main(String[] args) throws Exception { + + Security.setProperty("jdk.tls.disabledAlgorithms", ""); + if (args.length == 0) { + KDC kdc = KDC.create(REALM, HOST, 0, true); + + kdc.addPrincipal(USER, PASS); + kdc.addPrincipalRandKey("krbtgt/" + REALM); + kdc.addPrincipalRandKey(SERVER); + KDC.saveConfig(KRB5_CONF, kdc); + kdc.writeKtab(KTAB); + + File f = new File(JAAS_CONF); + FileOutputStream fos = new FileOutputStream(f); + fos.write(( + "ssl {\n" + + " com.sun.security.auth.module.Krb5LoginModule required\n" + + " principal=\"" + SERVER + "\"\n" + + " useKeyTab=true\n" + + " keyTab=" + KTAB + "\n" + + " isInitiator=false\n" + + " storeKey=true;\n};\n" + ).getBytes()); + fos.close(); + + Proc pc = Proc.create("SSLwithPerms") + .args("client") + .inheritIO() + .prop("java.security.manager", "") + .prop("java.security.krb5.conf", KRB5_CONF) + .prop("sun.net.spi.nameservice.provider.1", "ns,mock") + .prop("javax.net.ssl", "handshake") + .prop("sun.security.krb5.debug", "true") + .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms")) + .perm(new PropertyPermission("sun.security.krb5.principal", "read")) + .perm(new FilePermission("port", "read")) + .perm(new FilePermission(KTAB, "read")) + .perm(new RuntimePermission("accessClassInPackage.sun.net.spi.nameservice")) + .perm(new AuthPermission("modifyPrincipals")) + .perm(new AuthPermission("modifyPrivateCredentials")) + .perm(new AuthPermission("doAs")) + .perm(new SocketPermission("127.0.0.1", "connect")) + .perm(new ServicePermission("host/host.realm@REALM", "initiate")) + .start(); + + Proc ps = Proc.create("SSLwithPerms") + .args("server") + .inheritIO() + .prop("java.security.manager", "") + .prop("java.security.krb5.conf", KRB5_CONF) + .prop("java.security.auth.login.config", JAAS_CONF) + .prop("javax.net.ssl", "handshake") + .prop("sun.security.krb5.debug", "true") + .perm(new SecurityPermission("setProperty.jdk.tls.disabledAlgorithms")) + .perm(new AuthPermission("createLoginContext.ssl")) + .perm(new AuthPermission("doAs")) + .perm(new FilePermission("port", "write")) + .perm(new SocketPermission("127.0.0.1", "accept")) + .perm(new ServicePermission("host/host.realm@REALM", "accept")) + .start(); + + if (pc.waitFor() != 0) { + throw new Exception(); + } + if (ps.waitFor() != 0) { + throw new Exception(); + } + } else if (args[0].equals("client")) { + Context c; + c = Context.fromUserPass(USER, PASS, false); + c.doAs(new JsseClientAction(), null); + } else if (args[0].equals("server")) { + final Context s = Context.fromJAAS("ssl"); + s.doAs(new JsseServerAction(), null); + } + } + + private static class JsseClientAction implements Action { + public byte[] run(Context s, byte[] input) throws Exception { + SSLSocketFactory sslsf = + (SSLSocketFactory) SSLSocketFactory.getDefault(); + while (!Files.exists(Paths.get("port"))) { + Thread.sleep(100); + } + int port = ByteBuffer.allocate(4) + .put(Files.readAllBytes(Paths.get("port"))).getInt(0); + System.out.println("Connecting " + SERVER + ":" + port); + SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(HOST, port); + + // Enable only a KRB5 cipher suite. + String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"}; + sslSocket.setEnabledCipherSuites(enabledSuites); + + SSLParameters params = sslSocket.getSSLParameters(); + params.setServerNames(Collections.singletonList(new SNIHostName(HOST))); + sslSocket.setSSLParameters(params); + + BufferedReader in = new BufferedReader(new InputStreamReader( + sslSocket.getInputStream())); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter( + sslSocket.getOutputStream())); + + String outStr = "Hello There!\n"; + out.write(outStr); + out.flush(); + System.out.print("Sending " + outStr); + + String inStr = in.readLine(); + System.out.println("Received " + inStr); + + String cipherSuiteChosen = sslSocket.getSession().getCipherSuite(); + System.out.println("Cipher suite in use: " + cipherSuiteChosen); + Principal self = sslSocket.getSession().getLocalPrincipal(); + System.out.println("I am: " + self.toString()); + Principal peer = sslSocket.getSession().getPeerPrincipal(); + System.out.println("Server is: " + peer.toString()); + + sslSocket.close(); + return null; + } + } + + private static class JsseServerAction implements Action { + public byte[] run(Context s, byte[] input) throws Exception { + SSLServerSocketFactory sslssf = + (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); + SSLServerSocket sslServerSocket = + (SSLServerSocket) sslssf.createServerSocket(0); // any port + int port = sslServerSocket.getLocalPort(); + System.out.println("Listening on " + port); + + String enabledSuites[] = {"TLS_KRB5_WITH_RC4_128_SHA"}; + sslServerSocket.setEnabledCipherSuites(enabledSuites); + + Files.write(Paths.get("port"), ByteBuffer.allocate(4).putInt(port).array()); + System.out.println("Waiting for incoming connection..."); + + SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept(); + + System.out.println("Got connection from client " + + sslSocket.getInetAddress()); + + BufferedReader in = new BufferedReader(new InputStreamReader( + sslSocket.getInputStream())); + BufferedWriter out = new BufferedWriter(new OutputStreamWriter( + sslSocket.getOutputStream())); + + String inStr = in.readLine(); + System.out.println("Received " + inStr); + + String outStr = inStr + " " + new Date().toString() + "\n"; + out.write(outStr); + System.out.println("Sending " + outStr); + out.flush(); + + String cipherSuiteChosen = + sslSocket.getSession().getCipherSuite(); + System.out.println("Cipher suite in use: " + cipherSuiteChosen); + Principal self = sslSocket.getSession().getLocalPrincipal(); + System.out.println("I am: " + self.toString()); + Principal peer = sslSocket.getSession().getPeerPrincipal(); + System.out.println("Client is: " + peer.toString()); + + sslSocket.close(); + return null; + } + } +}