diff --git a/jdk/make/sun/security/other/Makefile b/jdk/make/sun/security/other/Makefile index 2722fbc32cd..a54c4aabbd6 100644 --- a/jdk/make/sun/security/other/Makefile +++ b/jdk/make/sun/security/other/Makefile @@ -39,6 +39,7 @@ AUTO_FILES_JAVA_DIRS = \ sun/security/provider \ sun/security/rsa \ sun/security/ssl \ + sun/security/ssl/krb5 \ sun/security/timestamp \ sun/security/validator \ sun/security/x509 \ diff --git a/jdk/src/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java b/jdk/src/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java index 85330c651f1..5f089c56332 100644 --- a/jdk/src/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java +++ b/jdk/src/share/classes/com/sun/jndi/ldap/ext/StartTlsResponseImpl.java @@ -40,7 +40,6 @@ import java.util.List; import java.security.Principal; import java.security.cert.X509Certificate; import java.security.cert.CertificateException; -import javax.security.auth.kerberos.KerberosPrincipal; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; @@ -413,14 +412,15 @@ final public class StartTlsResponseImpl extends StartTlsResponse { try { HostnameChecker checker = HostnameChecker.getInstance( HostnameChecker.TYPE_LDAP); - Principal principal = getPeerPrincipal(session); - if (principal instanceof KerberosPrincipal) { - if (!checker.match(hostname, (KerberosPrincipal) principal)) { + // Use ciphersuite to determine whether Kerberos is active. + if (session.getCipherSuite().startsWith("TLS_KRB5")) { + Principal principal = getPeerPrincipal(session); + if (!checker.match(hostname, principal)) { throw new SSLPeerUnverifiedException( "hostname of the kerberos principal:" + principal + " does not match the hostname:" + hostname); } - } else { + } else { // X.509 // get the subject's certificate certs = session.getPeerCertificates(); diff --git a/jdk/src/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java b/jdk/src/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java index e0512a33b87..b2459cb71f2 100644 --- a/jdk/src/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java +++ b/jdk/src/share/classes/com/sun/net/ssl/internal/www/protocol/https/DelegateHttpsURLConnection.java @@ -36,7 +36,6 @@ import java.security.Principal; import java.security.cert.*; import javax.security.auth.x500.X500Principal; -import javax.security.auth.kerberos.KerberosPrincipal; import sun.security.util.HostnameChecker; import sun.security.util.DerValue; @@ -109,20 +108,18 @@ class VerifierWrapper implements javax.net.ssl.HostnameVerifier { /* * In com.sun.net.ssl.HostnameVerifier the method is defined * as verify(String urlHostname, String certHostname). - * This means we need to extract the hostname from the certificate - * in this wrapper + * This means we need to extract the hostname from the X.509 certificate + * or from the Kerberos principal name, in this wrapper. */ public boolean verify(String hostname, javax.net.ssl.SSLSession session) { try { String serverName; - Principal principal = getPeerPrincipal(session); - // X.500 principal or Kerberos principal. - // (Use ciphersuite check to determine whether Kerberos is present.) - if (session.getCipherSuite().startsWith("TLS_KRB5") && - principal instanceof KerberosPrincipal) { + // Use ciphersuite to determine whether Kerberos is active. + if (session.getCipherSuite().startsWith("TLS_KRB5")) { serverName = - HostnameChecker.getServerName((KerberosPrincipal)principal); - } else { + HostnameChecker.getServerName(getPeerPrincipal(session)); + + } else { // X.509 Certificate[] serverChain = session.getPeerCertificates(); if ((serverChain == null) || (serverChain.length == 0)) { return false; diff --git a/jdk/src/share/classes/sun/net/www/protocol/https/HttpsClient.java b/jdk/src/share/classes/sun/net/www/protocol/https/HttpsClient.java index 8bbcb4de312..8c5ee25a959 100644 --- a/jdk/src/share/classes/sun/net/www/protocol/https/HttpsClient.java +++ b/jdk/src/share/classes/sun/net/www/protocol/https/HttpsClient.java @@ -54,7 +54,6 @@ import java.util.Iterator; import java.security.AccessController; import javax.security.auth.x500.X500Principal; -import javax.security.auth.kerberos.KerberosPrincipal; import javax.net.ssl.*; import sun.security.x509.X500Name; @@ -466,16 +465,14 @@ final class HttpsClient extends HttpClient HostnameChecker checker = HostnameChecker.getInstance( HostnameChecker.TYPE_TLS); - Principal principal = getPeerPrincipal(); - // X.500 principal or Kerberos principal. - // (Use ciphersuite check to determine whether Kerberos is present.) - if (cipher.startsWith("TLS_KRB5") && - principal instanceof KerberosPrincipal) { - if (!checker.match(host, (KerberosPrincipal)principal)) { + // Use ciphersuite to determine whether Kerberos is present. + if (cipher.startsWith("TLS_KRB5")) { + if (!checker.match(host, getPeerPrincipal())) { throw new SSLPeerUnverifiedException("Hostname checker" + " failed for Kerberos"); } - } else { + } else { // X.509 + // get the subject's certificate peerCerts = session.getPeerCertificates(); diff --git a/jdk/src/share/classes/sun/security/ssl/CipherSuite.java b/jdk/src/share/classes/sun/security/ssl/CipherSuite.java index 6ca15478f8e..13cbb547eb7 100644 --- a/jdk/src/share/classes/sun/security/ssl/CipherSuite.java +++ b/jdk/src/share/classes/sun/security/ssl/CipherSuite.java @@ -74,7 +74,7 @@ final class CipherSuite implements Comparable { // Flag indicating if CipherSuite availability can change dynamically. // This is the case when we rely on a JCE cipher implementation that // may not be available in the installed JCE providers. - // It is true because we might not have an ECC or Kerberos implementation. + // It is true because we might not have an ECC implementation. final static boolean DYNAMIC_AVAILABILITY = true; private final static boolean ALLOW_ECC = Debug.getBooleanProperty diff --git a/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java b/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java index 08877ba1c83..11f2da32c66 100644 --- a/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java +++ b/jdk/src/share/classes/sun/security/ssl/ClientHandshaker.java @@ -44,9 +44,6 @@ import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.*; import javax.security.auth.Subject; -import javax.security.auth.kerberos.KerberosPrincipal; -import sun.security.jgss.krb5.Krb5Util; -import sun.security.jgss.GSSCaller; import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager; @@ -362,9 +359,7 @@ final class ClientHandshaker extends Handshaker { subject = AccessController.doPrivileged( new PrivilegedExceptionAction() { public Subject run() throws Exception { - return Krb5Util.getSubject( - GSSCaller.CALLER_SSL_CLIENT, - getAccSE()); + return Krb5Helper.getClientSubject(getAccSE()); }}); } catch (PrivilegedActionException e) { subject = null; @@ -375,8 +370,9 @@ final class ClientHandshaker extends Handshaker { } if (subject != null) { - Set principals = - subject.getPrincipals(KerberosPrincipal.class); + // Eliminate dependency on KerberosPrincipal + Set principals = + subject.getPrincipals(Principal.class); if (!principals.contains(localPrincipal)) { throw new SSLProtocolException("Server resumed" + " session with wrong subject identity"); @@ -754,7 +750,7 @@ final class ClientHandshaker extends Handshaker { case K_KRB5: case K_KRB5_EXPORT: byte[] secretBytes = - ((KerberosClientKeyExchange)m2).getPreMasterSecret().getUnencrypted(); + ((KerberosClientKeyExchange)m2).getUnencryptedPreMasterSecret(); preMasterSecret = new SecretKeySpec(secretBytes, "TlsPremasterSecret"); break; case K_DHE_RSA: diff --git a/jdk/src/share/classes/sun/security/ssl/Debug.java b/jdk/src/share/classes/sun/security/ssl/Debug.java index 0840a0eb4f4..0357ecefacc 100644 --- a/jdk/src/share/classes/sun/security/ssl/Debug.java +++ b/jdk/src/share/classes/sun/security/ssl/Debug.java @@ -1,5 +1,5 @@ /* - * Copyright 1999-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1999-2009 Sun Microsystems, Inc. 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 @@ -160,7 +160,7 @@ public class Debug { System.err.println(prefix + ": "+message); } - static void println(PrintStream s, String name, byte[] data) { + public static void println(PrintStream s, String name, byte[] data) { s.print(name + ": { "); if (data == null) { s.print("null"); diff --git a/jdk/src/share/classes/sun/security/ssl/HandshakeInStream.java b/jdk/src/share/classes/sun/security/ssl/HandshakeInStream.java index 84ed819769b..bcb3386b5bf 100644 --- a/jdk/src/share/classes/sun/security/ssl/HandshakeInStream.java +++ b/jdk/src/share/classes/sun/security/ssl/HandshakeInStream.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2009 Sun Microsystems, Inc. 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 @@ -52,7 +52,7 @@ import javax.net.ssl.SSLException; * * @author David Brownell */ -class HandshakeInStream extends InputStream { +public class HandshakeInStream extends InputStream { InputRecord r; @@ -196,7 +196,7 @@ class HandshakeInStream extends InputStream { return b; } - byte[] getBytes16() throws IOException { + public byte[] getBytes16() throws IOException { int len = getInt16(); byte b[] = new byte[len]; diff --git a/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java b/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java index 7f828bae5d1..0e080d32bc3 100644 --- a/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java +++ b/jdk/src/share/classes/sun/security/ssl/HandshakeMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2009 Sun Microsystems, Inc. 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 @@ -23,7 +23,6 @@ * have any questions. */ - package sun.security.ssl; import java.io.*; @@ -73,7 +72,7 @@ import sun.security.ssl.CipherSuite.*; * * @author David Brownell */ -abstract class HandshakeMessage { +public abstract class HandshakeMessage { HandshakeMessage() { } @@ -92,7 +91,7 @@ abstract class HandshakeMessage { static final byte ht_finished = 20; /* Class and subclass dynamic debugging support */ - static final Debug debug = Debug.getInstance("ssl"); + public static final Debug debug = Debug.getInstance("ssl"); /** * Utility method to convert a BigInteger to a byte array in unsigned @@ -468,7 +467,6 @@ class CertificateMsg extends HandshakeMessage } } - /* * ServerKeyExchange ... SERVER --> CLIENT * diff --git a/jdk/src/share/classes/sun/security/ssl/HandshakeOutStream.java b/jdk/src/share/classes/sun/security/ssl/HandshakeOutStream.java index 9f8a269495b..0854fb6f152 100644 --- a/jdk/src/share/classes/sun/security/ssl/HandshakeOutStream.java +++ b/jdk/src/share/classes/sun/security/ssl/HandshakeOutStream.java @@ -1,5 +1,5 @@ /* - * Copyright 1996-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1996-2009 Sun Microsystems, Inc. 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 @@ -41,7 +41,7 @@ import java.security.MessageDigest; * * @author David Brownell */ -class HandshakeOutStream extends OutputStream { +public class HandshakeOutStream extends OutputStream { private SSLSocketImpl socket; private SSLEngineImpl engine; @@ -196,7 +196,7 @@ class HandshakeOutStream extends OutputStream { write(b, 0, b.length); } - void putBytes16(byte b[]) throws IOException { + public void putBytes16(byte b[]) throws IOException { if (b == null) { putInt16(0); return; diff --git a/jdk/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java b/jdk/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java index e79026e48e4..b9719754359 100644 --- a/jdk/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java +++ b/jdk/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java @@ -29,199 +29,67 @@ 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.Principal; +import java.security.PrivilegedAction; import java.security.SecureRandom; -import java.net.InetAddress; - -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.Realm; -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 javax.crypto.SecretKey; /** - * 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 - * + * A helper class that calls the KerberosClientKeyExchange implementation. */ -final class KerberosClientKeyExchange extends HandshakeMessage { +public class KerberosClientKeyExchange extends HandshakeMessage { - private KerberosPreMasterSecret preMaster; - private byte[] encodedTicket; - private KerberosPrincipal peerPrincipal; - private KerberosPrincipal localPrincipal; + private static final String IMPL_CLASS = + "sun.security.ssl.krb5.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 - */ - KerberosClientKeyExchange(String serverName, boolean isLoopback, + private static final Class implClass = AccessController.doPrivileged( + new PrivilegedAction>() { + 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 (getClass() == KerberosClientKeyExchange.class) { + try { + return (KerberosClientKeyExchange)implClass.newInstance(); + } catch (InstantiationException e) { + throw new AssertionError(e); + } catch (IllegalAccessException e) { + throw new AssertionError(e); + } + } + return null; + } + + public KerberosClientKeyExchange() { + } + + public KerberosClientKeyExchange(String serverName, boolean isLoopback, AccessControlContext acc, ProtocolVersion protocolVersion, SecureRandom rand) throws IOException { - // Get service ticket - KerberosTicket ticket = getServiceTicket(serverName, isLoopback, 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); + if (impl != null) { + init(serverName, isLoopback, acc, protocolVersion, rand); + } else { + throw new IllegalStateException("Kerberos is unavailable"); + } } - /** - * 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 serverKey server's master secret key - */ - KerberosClientKeyExchange(ProtocolVersion protocolVersion, - ProtocolVersion clientVersion, - SecureRandom rand, HandshakeInStream input, KerberosKey[] serverKeys) - throws IOException { + public KerberosClientKeyExchange(ProtocolVersion protocolVersion, + ProtocolVersion clientVersion, SecureRandom rand, + HandshakeInStream input, SecretKey[] serverKeys) 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; - Realm ticketRealm = t.realm; - - String serverPrincipal = serverKeys[0].getPrincipal().getName(); - - /* - * 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 - */ - - // Check that ticket Sname matches serverPrincipal - String ticketPrinc = ticketSname.toString().concat("@" + - ticketRealm.toString()); - if (!ticketPrinc.equals(serverPrincipal)) { - if (debug != null && Debug.isOn("handshake")) - System.out.println("Service principal in Ticket does not" - + " match associated principal in KerberosKey"); - throw new IOException("Server principal is " + - serverPrincipal + " but ticket is for " + - ticketPrinc); - } - - // See if we have the right key to decrypt the ticket to get - // the session key. - int encPartKeyType = encPart.getEType(); - KerberosKey dkey = findKey(encPartKeyType, serverKeys); - 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, true); - 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: " + serverPrincipal); - System.out.println("realm: " + encTicketPart.crealm.toString()); - 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); + if (impl != null) { + init(protocolVersion, clientVersion, rand, input, serverKeys); } else { - // Generate bogus premaster secret - preMaster = new KerberosPreMasterSecret(protocolVersion, rand); + throw new IllegalStateException("Kerberos is unavailable"); } } @@ -229,138 +97,46 @@ final class KerberosClientKeyExchange extends HandshakeMessage { return ht_client_key_exchange; } - int messageLength() { - return (6 + encodedTicket.length + preMaster.getEncrypted().length); + public int messageLength() { + return impl.messageLength(); } - void send(HandshakeOutStream s) throws IOException { - s.putBytes16(encodedTicket); - s.putBytes16(null); // XXX no authenticator - s.putBytes16(preMaster.getEncrypted()); + public void send(HandshakeOutStream s) throws IOException { + impl.send(s); } - void print(PrintStream s) throws IOException { - s.println("*** ClientKeyExchange, Kerberos"); + @Override + public void print(PrintStream p) throws IOException { + impl.print(p); + } - 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()); + public void init(String serverName, boolean isLoopback, + AccessControlContext acc, ProtocolVersion protocolVersion, + SecureRandom rand) throws IOException { + + if (impl != null) { + impl.init(serverName, isLoopback, acc, protocolVersion, rand); } } - // Similar to sun.security.jgss.krb5.Krb5InitCredenetial/Krb5Context - private static KerberosTicket getServiceTicket(String srvName, - boolean isLoopback, final AccessControlContext acc) throws IOException { + public void init(ProtocolVersion protocolVersion, + ProtocolVersion clientVersion, SecureRandom rand, + HandshakeInStream input, SecretKey[] serverKeys) throws IOException { - // get the local hostname if srvName is loopback address - String serverName = srvName; - if (isLoopback) { - String localHost = java.security.AccessController.doPrivileged( - new java.security.PrivilegedAction() { - public String run() { - String hostname; - try { - hostname = InetAddress.getLocalHost().getHostName(); - } catch (java.net.UnknownHostException e) { - hostname = "localhost"; - } - return hostname; - } - }); - 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; + if (impl != null) { + impl.init(protocolVersion, clientVersion, rand, input, serverKeys); } } - KerberosPreMasterSecret getPreMasterSecret() { - return preMaster; + public byte[] getUnencryptedPreMasterSecret() { + return impl.getUnencryptedPreMasterSecret(); } - KerberosPrincipal getPeerPrincipal() { - return peerPrincipal; + public Principal getPeerPrincipal(){ + return impl.getPeerPrincipal(); } - KerberosPrincipal getLocalPrincipal() { - return localPrincipal; - } - - private static KerberosKey findKey(int etype, KerberosKey[] keys) { - int ktype; - for (int i = 0; i < keys.length; i++) { - ktype = keys[i].getKeyType(); - if (etype == ktype) { - return keys[i]; - } - } - // 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) { - return new KerberosKey(keys[i].getPrincipal(), - keys[i].getEncoded(), - etype, - keys[i].getVersionNumber()); - } - } - } - return null; + public Principal getLocalPrincipal(){ + return impl.getLocalPrincipal(); } } diff --git a/jdk/src/share/classes/sun/security/ssl/Krb5Helper.java b/jdk/src/share/classes/sun/security/ssl/Krb5Helper.java new file mode 100644 index 00000000000..ffad5e763ca --- /dev/null +++ b/jdk/src/share/classes/sun/security/ssl/Krb5Helper.java @@ -0,0 +1,126 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.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() { + 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 SecretKey[] getServerKeys(AccessControlContext acc) + throws LoginException { + ensureAvailable(); + return proxy.getServerKeys(acc); + } + + /** + * Returns the server-side principal name associated with the KerberosKey. + */ + public static String getServerPrincipalName(SecretKey kerberosKey) { + ensureAvailable(); + return proxy.getServerPrincipalName(kerberosKey); + } + + /** + * 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); + } +} diff --git a/jdk/src/share/classes/sun/security/ssl/Krb5Proxy.java b/jdk/src/share/classes/sun/security/ssl/Krb5Proxy.java new file mode 100644 index 00000000000..535ca5bded5 --- /dev/null +++ b/jdk/src/share/classes/sun/security/ssl/Krb5Proxy.java @@ -0,0 +1,71 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.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 KerberosKeys for the default server-side principal. + */ + SecretKey[] getServerKeys(AccessControlContext acc) throws LoginException; + + /** + * Returns the server-side principal name associated with the KerberosKey. + */ + String getServerPrincipalName(SecretKey kerberosKey); + + /** + * 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); +} diff --git a/jdk/src/share/classes/sun/security/ssl/ProtocolVersion.java b/jdk/src/share/classes/sun/security/ssl/ProtocolVersion.java index 11aacd51504..8a4b3cf385a 100644 --- a/jdk/src/share/classes/sun/security/ssl/ProtocolVersion.java +++ b/jdk/src/share/classes/sun/security/ssl/ProtocolVersion.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2002-2009 Sun Microsystems, Inc. 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 @@ -45,7 +45,7 @@ package sun.security.ssl; * @author Andreas Sterbenz * @since 1.4.1 */ -final class ProtocolVersion { +public final class ProtocolVersion { // dummy protocol version value for invalid SSLSession final static ProtocolVersion NONE = new ProtocolVersion(-1, "NONE"); @@ -80,10 +80,10 @@ final class ProtocolVersion { // version in 16 bit MSB format as it appears in records and // messages, i.e. 0x0301 for TLS 1.0 - final int v; + public final int v; // major and minor version - final byte major, minor; + public final byte major, minor; // name used in JSSE (e.g. TLSv1 for TLS 1.0) final String name; @@ -117,7 +117,7 @@ final class ProtocolVersion { * Return a ProtocolVersion with the specified major and minor version * numbers. Never throws exceptions. */ - static ProtocolVersion valueOf(int major, int minor) { + public static ProtocolVersion valueOf(int major, int minor) { major &= 0xff; minor &= 0xff; int v = (major << 8) | minor; diff --git a/jdk/src/share/classes/sun/security/ssl/SSLSessionImpl.java b/jdk/src/share/classes/sun/security/ssl/SSLSessionImpl.java index a8cd8c76a51..57f23aa77f0 100644 --- a/jdk/src/share/classes/sun/security/ssl/SSLSessionImpl.java +++ b/jdk/src/share/classes/sun/security/ssl/SSLSessionImpl.java @@ -48,7 +48,6 @@ import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLPermission; -import javax.security.auth.kerberos.KerberosPrincipal; import javax.security.auth.x500.X500Principal; import static sun.security.ssl.CipherSuite.*; @@ -469,8 +468,8 @@ final class SSLSessionImpl implements SSLSession { * defining the session. * * @return the peer's principal. Returns an X500Principal of the - * end-entity certiticate for X509-based cipher suites, and - * KerberosPrincipal for Kerberos cipher suites. + * end-entity certificate for X509-based cipher suites, and + * Principal for Kerberos cipher suites. * * @throws SSLPeerUnverifiedException if the peer's identity has not * been verified @@ -483,7 +482,8 @@ final class SSLSessionImpl implements SSLSession { if (peerPrincipal == null) { throw new SSLPeerUnverifiedException("peer not authenticated"); } else { - return (KerberosPrincipal)peerPrincipal; + // Eliminate dependency on KerberosPrincipal + return peerPrincipal; } } if (peerCerts == null) { @@ -497,15 +497,15 @@ final class SSLSessionImpl implements SSLSession { * * @return the principal sent to the peer. Returns an X500Principal * of the end-entity certificate for X509-based cipher suites, and - * KerberosPrincipal for Kerberos cipher suites. If no principal was + * Principal for Kerberos cipher suites. If no principal was * sent, then null is returned. */ public Principal getLocalPrincipal() { if ((cipherSuite.keyExchange == K_KRB5) || (cipherSuite.keyExchange == K_KRB5_EXPORT)) { - return (localPrincipal == null ? null : - (KerberosPrincipal)localPrincipal); + // Eliminate dependency on KerberosPrincipal + return (localPrincipal == null ? null : localPrincipal); } return (localCerts == null ? null : localCerts[0].getSubjectX500Principal()); diff --git a/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java b/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java index 772cdb65838..594db0e74d9 100644 --- a/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java +++ b/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java @@ -39,11 +39,6 @@ import javax.crypto.spec.SecretKeySpec; import javax.net.ssl.*; import javax.security.auth.Subject; -import javax.security.auth.kerberos.KerberosKey; -import javax.security.auth.kerberos.KerberosPrincipal; -import javax.security.auth.kerberos.ServicePermission; -import sun.security.jgss.krb5.Krb5Util; -import sun.security.jgss.GSSCaller; import com.sun.net.ssl.internal.ssl.X509ExtendedTrustManager; @@ -69,7 +64,7 @@ final class ServerHandshaker extends Handshaker { private X509Certificate[] certs; private PrivateKey privateKey; - private KerberosKey[] kerberosKeys; + private SecretKey[] kerberosKeys; // flag to check for clientCertificateVerify message private boolean needClientVerify = false; @@ -366,9 +361,8 @@ final class ServerHandshaker extends Handshaker { subject = AccessController.doPrivileged( new PrivilegedExceptionAction() { public Subject run() throws Exception { - return Krb5Util.getSubject( - GSSCaller.CALLER_SSL_SERVER, - getAccSE()); + return + Krb5Helper.getServerSubject(getAccSE()); }}); } catch (PrivilegedActionException e) { subject = null; @@ -379,8 +373,9 @@ final class ServerHandshaker extends Handshaker { } if (subject != null) { - Set principals = - subject.getPrincipals(KerberosPrincipal.class); + // Eliminate dependency on KerberosPrincipal + Set principals = + subject.getPrincipals(Principal.class); if (!principals.contains(localPrincipal)) { resumingSession = false; if (debug != null && Debug.isOn("session")) { @@ -914,11 +909,11 @@ final class ServerHandshaker extends Handshaker { try { final AccessControlContext acc = getAccSE(); kerberosKeys = AccessController.doPrivileged( - new PrivilegedExceptionAction() { - public KerberosKey[] run() throws Exception { + // Eliminate dependency on KerberosKey + new PrivilegedExceptionAction() { + public SecretKey[] run() throws Exception { // get kerberos key for the default principal - return Krb5Util.getKeys( - GSSCaller.CALLER_SSL_SERVER, null, acc); + return Krb5Helper.getServerKeys(acc); }}); // check permission to access and use the secret key of the @@ -931,12 +926,13 @@ final class ServerHandshaker extends Handshaker { } String serverPrincipal = - kerberosKeys[0].getPrincipal().getName(); + Krb5Helper.getServerPrincipalName(kerberosKeys[0]); SecurityManager sm = System.getSecurityManager(); try { if (sm != null) { - sm.checkPermission(new ServicePermission(serverPrincipal, - "accept"), acc); + // Eliminate dependency on ServicePermission + sm.checkPermission(Krb5Helper.getServicePermission( + serverPrincipal, "accept"), acc); } } catch (SecurityException se) { kerberosKeys = null; @@ -973,7 +969,7 @@ final class ServerHandshaker extends Handshaker { session.setPeerPrincipal(mesg.getPeerPrincipal()); session.setLocalPrincipal(mesg.getLocalPrincipal()); - byte[] b = mesg.getPreMasterSecret().getUnencrypted(); + byte[] b = mesg.getUnencryptedPreMasterSecret(); return new SecretKeySpec(b, "TlsPremasterSecret"); } diff --git a/jdk/src/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java b/jdk/src/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java new file mode 100644 index 00000000000..d0f95758c49 --- /dev/null +++ b/jdk/src/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java @@ -0,0 +1,383 @@ +/* + * Copyright 2003-2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.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 javax.crypto.SecretKey; +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.Realm; +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.ssl.Debug; +import sun.security.ssl.HandshakeInStream; +import sun.security.ssl.HandshakeOutStream; +import sun.security.ssl.KerberosClientKeyExchange; +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, boolean isLoopback, + AccessControlContext acc, ProtocolVersion protocolVersion, + SecureRandom rand) throws IOException { + + // Get service ticket + KerberosTicket ticket = getServiceTicket(serverName, isLoopback, 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 serverKey server's master secret key + */ + @Override + public void init(ProtocolVersion protocolVersion, + ProtocolVersion clientVersion, + SecureRandom rand, HandshakeInStream input, SecretKey[] secretKeys) + throws IOException { + + KerberosKey[] serverKeys = (KerberosKey[])secretKeys; + + // 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; + Realm ticketRealm = t.realm; + + String serverPrincipal = serverKeys[0].getPrincipal().getName(); + + /* + * 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 + */ + + // Check that ticket Sname matches serverPrincipal + String ticketPrinc = ticketSname.toString().concat("@" + + ticketRealm.toString()); + if (!ticketPrinc.equals(serverPrincipal)) { + if (debug != null && Debug.isOn("handshake")) + System.out.println("Service principal in Ticket does not" + + " match associated principal in KerberosKey"); + throw new IOException("Server principal is " + + serverPrincipal + " but ticket is for " + + ticketPrinc); + } + + // See if we have the right key to decrypt the ticket to get + // the session key. + int encPartKeyType = encPart.getEType(); + KerberosKey dkey = findKey(encPartKeyType, serverKeys); + 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, true); + 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: " + serverPrincipal); + System.out.println("realm: " + encTicketPart.crealm.toString()); + 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(protocolVersion, 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 srvName, + boolean isLoopback, final AccessControlContext acc) throws IOException { + + // get the local hostname if srvName is loopback address + String serverName = srvName; + if (isLoopback) { + String localHost = java.security.AccessController.doPrivileged( + new java.security.PrivilegedAction() { + public String run() { + String hostname; + try { + hostname = InetAddress.getLocalHost().getHostName(); + } catch (java.net.UnknownHostException e) { + hostname = "localhost"; + } + return hostname; + } + }); + 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; + } + + private static KerberosKey findKey(int etype, KerberosKey[] keys) { + int ktype; + for (int i = 0; i < keys.length; i++) { + ktype = keys[i].getKeyType(); + if (etype == ktype) { + return keys[i]; + } + } + // 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) { + return new KerberosKey(keys[i].getPrincipal(), + keys[i].getEncoded(), + etype, + keys[i].getVersionNumber()); + } + } + } + return null; + } +} diff --git a/jdk/src/share/classes/sun/security/ssl/KerberosPreMasterSecret.java b/jdk/src/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java similarity index 96% rename from jdk/src/share/classes/sun/security/ssl/KerberosPreMasterSecret.java rename to jdk/src/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java index db27b0ba059..aea9492f86d 100644 --- a/jdk/src/share/classes/sun/security/ssl/KerberosPreMasterSecret.java +++ b/jdk/src/share/classes/sun/security/ssl/krb5/KerberosPreMasterSecret.java @@ -1,5 +1,5 @@ /* - * Copyright 2003-2007 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 2003-2009 Sun Microsystems, Inc. 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 @@ -23,7 +23,7 @@ * have any questions. */ -package sun.security.ssl; +package sun.security.ssl.krb5; import java.io.*; import java.security.*; @@ -36,6 +36,11 @@ import sun.security.krb5.EncryptedData; import sun.security.krb5.KrbException; import sun.security.krb5.internal.crypto.KeyUsage; +import sun.security.ssl.Debug; +import sun.security.ssl.HandshakeInStream; +import sun.security.ssl.HandshakeMessage; +import sun.security.ssl.ProtocolVersion; + /** * This is the Kerberos premaster secret in the Kerberos client key * exchange message (CLIENT --> SERVER); it holds the @@ -211,12 +216,12 @@ final class KerberosPreMasterSecret { return pm; } - // Clone not needed; package internal use only + // Clone not needed; internal use only byte[] getUnencrypted() { return preMaster; } - // Clone not needed; package internal use only + // Clone not needed; internal use only byte[] getEncrypted() { return encrypted; } diff --git a/jdk/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java b/jdk/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java new file mode 100644 index 00000000000..f571845b320 --- /dev/null +++ b/jdk/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java @@ -0,0 +1,99 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.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 javax.crypto.SecretKey; +import javax.security.auth.Subject; +import javax.security.auth.kerberos.KerberosKey; +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.krb5.PrincipalName; +import sun.security.ssl.Krb5Proxy; + +/** + * An implementatin 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 SecretKey[] getServerKeys(AccessControlContext acc) + throws LoginException { + return Krb5Util.getKeys(GSSCaller.CALLER_SSL_SERVER, null, acc); + } + + @Override + public String getServerPrincipalName(SecretKey kerberosKey) { + return ((KerberosKey)kerberosKey).getPrincipal().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); + } +} diff --git a/jdk/src/share/classes/sun/security/util/HostnameChecker.java b/jdk/src/share/classes/sun/security/util/HostnameChecker.java index a4d1c15d1ca..286e2155875 100644 --- a/jdk/src/share/classes/sun/security/util/HostnameChecker.java +++ b/jdk/src/share/classes/sun/security/util/HostnameChecker.java @@ -32,10 +32,9 @@ import java.security.Principal; import java.security.cert.*; import javax.security.auth.x500.X500Principal; -import javax.security.auth.kerberos.KerberosPrincipal; +import sun.security.ssl.Krb5Helper; import sun.security.x509.X500Name; -import sun.security.krb5.PrincipalName; import sun.net.util.IPAddressUtil; @@ -98,8 +97,7 @@ public class HostnameChecker { /** * Perform the check for Kerberos. */ - public static boolean match(String expectedName, - KerberosPrincipal principal) { + public static boolean match(String expectedName, Principal principal) { String hostName = getServerName(principal); return (expectedName.equalsIgnoreCase(hostName)); } @@ -107,23 +105,8 @@ public class HostnameChecker { /** * Return the Server name from Kerberos principal. */ - public static String getServerName(KerberosPrincipal 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; + public static String getServerName(Principal principal) { + return Krb5Helper.getPrincipalHostName(principal); } /** diff --git a/jdk/test/sun/security/krb5/auto/Action.java b/jdk/test/sun/security/krb5/auto/Action.java index 1e7073dc1ff..9a67e3fa2db 100644 --- a/jdk/test/sun/security/krb5/auto/Action.java +++ b/jdk/test/sun/security/krb5/auto/Action.java @@ -30,4 +30,3 @@ public interface Action { */ byte[] run(Context s, byte[] input) throws Exception; } - diff --git a/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java b/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java index f50fb55855c..8d4b2ab6fe7 100644 --- a/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java +++ b/jdk/test/sun/security/krb5/auto/HttpNegotiateServer.java @@ -302,4 +302,3 @@ public class HttpNegotiateServer { } } } - diff --git a/jdk/test/sun/security/krb5/auto/SSL.java b/jdk/test/sun/security/krb5/auto/SSL.java new file mode 100644 index 00000000000..44a80a60ae9 --- /dev/null +++ b/jdk/test/sun/security/krb5/auto/SSL.java @@ -0,0 +1,168 @@ +/* + * Copyright 2009 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6894643 + * @summary Test JSSE Kerberos ciphersuite + */ +import java.io.*; +import java.net.InetAddress; +import javax.net.ssl.*; +import java.security.Principal; +import java.util.Date; +import sun.security.jgss.GSSUtil; + +public class SSL { + + private static final String KRB5_CIPHER = "TLS_KRB5_WITH_3DES_EDE_CBC_SHA"; + private static final int PORT = 4569; + private static final int LOOP_LIMIT = 1; + private static final char[] PASS = "secret".toCharArray(); + private static int loopCount = 0; + + private static String SERVER; + + public static void main(String[] args) throws Exception { + + KDC kdc = KDC.create(OneKDC.REALM); + // Run this after KDC, so our own DNS service can be started + try { + SERVER = InetAddress.getLocalHost().getHostName(); + } catch (java.net.UnknownHostException e) { + SERVER = "localhost"; + } + + kdc.addPrincipal(OneKDC.USER, OneKDC.PASS); + kdc.addPrincipalRandKey("krbtgt/" + OneKDC.REALM); + kdc.addPrincipal("host/" + SERVER, PASS); + KDC.saveConfig(OneKDC.KRB5_CONF, kdc); + System.setProperty("java.security.krb5.conf", OneKDC.KRB5_CONF); + + final Context c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false); + final Context s = Context.fromUserPass("host/" + SERVER, PASS, true); + + c.startAsClient("host/" + SERVER, GSSUtil.GSS_KRB5_MECH_OID); + s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID); + + new Thread(new Runnable() { + public void run() { + try { + s.doAs(new JsseServerAction(), null); + } catch (Exception e) { + e.printStackTrace(); + } + } + }).start(); + + // Warm the server + Thread.sleep(2000); + + c.doAs(new JsseClientAction(), null); + } + + // Following codes copied from + // http://java.sun.com/javase/6/docs/technotes/guides/security/jgss/lab/part2.html#JSSE + private static class JsseClientAction implements Action { + public byte[] run(Context s, byte[] input) throws Exception { + SSLSocketFactory sslsf = + (SSLSocketFactory) SSLSocketFactory.getDefault(); + SSLSocket sslSocket = (SSLSocket) sslsf.createSocket(SERVER, PORT); + + // Enable only a KRB5 cipher suite. + String enabledSuites[] = {KRB5_CIPHER}; + sslSocket.setEnabledCipherSuites(enabledSuites); + // Should check for exception if enabledSuites is not supported + + 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(PORT); + + // Enable only a KRB5 cipher suite. + String enabledSuites[] = {KRB5_CIPHER}; + sslServerSocket.setEnabledCipherSuites(enabledSuites); + // Should check for exception if enabledSuites is not supported + + while (loopCount++ < LOOP_LIMIT) { + 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; + } + } +}