8038089: TLS optional support for Kerberos cipher suites needs to be re-examine
Reviewed-by: xuelei
This commit is contained in:
parent
0f1698f906
commit
1f705ef606
@ -44,8 +44,6 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
|
|
||||||
import javax.net.ssl.*;
|
import javax.net.ssl.*;
|
||||||
|
|
||||||
import javax.security.auth.Subject;
|
|
||||||
|
|
||||||
import sun.security.ssl.HandshakeMessage.*;
|
import sun.security.ssl.HandshakeMessage.*;
|
||||||
import static sun.security.ssl.CipherSuite.KeyExchange.*;
|
import static sun.security.ssl.CipherSuite.KeyExchange.*;
|
||||||
|
|
||||||
@ -234,7 +232,8 @@ final class ClientHandshaker extends Handshaker {
|
|||||||
|
|
||||||
case HandshakeMessage.ht_certificate:
|
case HandshakeMessage.ht_certificate:
|
||||||
if (keyExchange == K_DH_ANON || keyExchange == K_ECDH_ANON
|
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,
|
fatalSE(Alerts.alert_unexpected_message,
|
||||||
"unexpected server cert chain");
|
"unexpected server cert chain");
|
||||||
// NOTREACHED
|
// NOTREACHED
|
||||||
@ -333,13 +332,9 @@ final class ClientHandshaker extends Handshaker {
|
|||||||
throw new SSLProtocolException(
|
throw new SSLProtocolException(
|
||||||
"Protocol violation: server sent a server key exchange"
|
"Protocol violation: server sent a server key exchange"
|
||||||
+ " message for key exchange " + keyExchange);
|
+ " message for key exchange " + keyExchange);
|
||||||
case K_KRB5:
|
|
||||||
case K_KRB5_EXPORT:
|
|
||||||
throw new SSLProtocolException(
|
|
||||||
"unexpected receipt of server key exchange algorithm");
|
|
||||||
default:
|
default:
|
||||||
throw new SSLProtocolException(
|
throw new SSLProtocolException(
|
||||||
"unsupported key exchange algorithm = "
|
"unsupported or unexpected key exchange algorithm = "
|
||||||
+ keyExchange);
|
+ keyExchange);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -350,10 +345,11 @@ final class ClientHandshaker extends Handshaker {
|
|||||||
throw new SSLHandshakeException(
|
throw new SSLHandshakeException(
|
||||||
"Client authentication requested for "+
|
"Client authentication requested for "+
|
||||||
"anonymous cipher suite.");
|
"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(
|
throw new SSLHandshakeException(
|
||||||
"Client certificate requested for "+
|
"Client certificate requested for "+
|
||||||
"kerberos cipher suite.");
|
"external cipher suite: " + keyExchange);
|
||||||
}
|
}
|
||||||
certRequest = new CertificateRequest(input, protocolVersion);
|
certRequest = new CertificateRequest(input, protocolVersion);
|
||||||
if (debug != null && Debug.isOn("handshake")) {
|
if (debug != null && Debug.isOn("handshake")) {
|
||||||
@ -626,45 +622,17 @@ final class ClientHandshaker extends Handshaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// validate subject identity
|
// validate subject identity
|
||||||
if (sessionSuite.keyExchange == K_KRB5 ||
|
ClientKeyExchangeService p =
|
||||||
sessionSuite.keyExchange == K_KRB5_EXPORT) {
|
ClientKeyExchangeService.find(sessionSuite.keyExchange.name);
|
||||||
|
if (p != null) {
|
||||||
Principal localPrincipal = session.getLocalPrincipal();
|
Principal localPrincipal = session.getLocalPrincipal();
|
||||||
|
|
||||||
Subject subject = null;
|
if (p.isRelated(true, getAccSE(), localPrincipal)) {
|
||||||
try {
|
|
||||||
subject = AccessController.doPrivileged(
|
|
||||||
new PrivilegedExceptionAction<Subject>() {
|
|
||||||
@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<Principal> 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 (debug != null && Debug.isOn("session"))
|
if (debug != null && Debug.isOn("session"))
|
||||||
System.out.println("Kerberos credentials are not" +
|
System.out.println("Subject identity is same");
|
||||||
" present in the current Subject; check if " +
|
} else {
|
||||||
" javax.security.auth.useSubjectAsCreds" +
|
throw new SSLProtocolException("Server resumed" +
|
||||||
" system property has been set to false");
|
" session with wrong subject identity or no subject");
|
||||||
throw new SSLProtocolException
|
|
||||||
("Server resumed session with no subject");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1012,8 +980,14 @@ final class ClientHandshaker extends Handshaker {
|
|||||||
ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
|
ecdh = new ECDHCrypt(params, sslContext.getSecureRandom());
|
||||||
m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
|
m2 = new ECDHClientKeyExchange(ecdh.getPublicKey());
|
||||||
break;
|
break;
|
||||||
case K_KRB5:
|
default:
|
||||||
case K_KRB5_EXPORT:
|
ClientKeyExchangeService p =
|
||||||
|
ClientKeyExchangeService.find(keyExchange.name);
|
||||||
|
if (p == null) {
|
||||||
|
// somethings very wrong
|
||||||
|
throw new RuntimeException
|
||||||
|
("Unsupported key exchange: " + keyExchange);
|
||||||
|
}
|
||||||
String sniHostname = null;
|
String sniHostname = null;
|
||||||
for (SNIServerName serverName : requestedServerNames) {
|
for (SNIServerName serverName : requestedServerNames) {
|
||||||
if (serverName instanceof SNIHostName) {
|
if (serverName instanceof SNIHostName) {
|
||||||
@ -1022,13 +996,13 @@ final class ClientHandshaker extends Handshaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
KerberosClientKeyExchange kerberosMsg = null;
|
ClientKeyExchange exMsg = null;
|
||||||
if (sniHostname != null) {
|
if (sniHostname != null) {
|
||||||
// use first requested SNI hostname
|
// use first requested SNI hostname
|
||||||
try {
|
try {
|
||||||
kerberosMsg = new KerberosClientKeyExchange(
|
exMsg = p.createClientExchange(
|
||||||
sniHostname, getAccSE(), protocolVersion,
|
sniHostname, getAccSE(), protocolVersion,
|
||||||
sslContext.getSecureRandom());
|
sslContext.getSecureRandom());
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
if (serverNamesAccepted) {
|
if (serverNamesAccepted) {
|
||||||
// server accepted requested SNI hostname,
|
// server accepted requested SNI hostname,
|
||||||
@ -1044,26 +1018,22 @@ final class ClientHandshaker extends Handshaker {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (kerberosMsg == null) {
|
if (exMsg == null) {
|
||||||
String hostname = getHostSE();
|
String hostname = getHostSE();
|
||||||
if (hostname == null) {
|
if (hostname == null) {
|
||||||
throw new IOException("Hostname is required" +
|
throw new IOException("Hostname is required" +
|
||||||
" to use Kerberos cipher suites");
|
" to use " + keyExchange + " key exchange");
|
||||||
}
|
}
|
||||||
kerberosMsg = new KerberosClientKeyExchange(
|
exMsg = p.createClientExchange(
|
||||||
hostname, getAccSE(), protocolVersion,
|
hostname, getAccSE(), protocolVersion,
|
||||||
sslContext.getSecureRandom());
|
sslContext.getSecureRandom());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the principals involved in exchange
|
// Record the principals involved in exchange
|
||||||
session.setPeerPrincipal(kerberosMsg.getPeerPrincipal());
|
session.setPeerPrincipal(exMsg.getPeerPrincipal());
|
||||||
session.setLocalPrincipal(kerberosMsg.getLocalPrincipal());
|
session.setLocalPrincipal(exMsg.getLocalPrincipal());
|
||||||
m2 = kerberosMsg;
|
m2 = exMsg;
|
||||||
break;
|
break;
|
||||||
default:
|
|
||||||
// somethings very wrong
|
|
||||||
throw new RuntimeException
|
|
||||||
("Unsupported key exchange: " + keyExchange);
|
|
||||||
}
|
}
|
||||||
if (debug != null && Debug.isOn("handshake")) {
|
if (debug != null && Debug.isOn("handshake")) {
|
||||||
m2.print(System.out);
|
m2.print(System.out);
|
||||||
@ -1094,13 +1064,6 @@ final class ClientHandshaker extends Handshaker {
|
|||||||
case K_RSA_EXPORT:
|
case K_RSA_EXPORT:
|
||||||
preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
|
preMasterSecret = ((RSAClientKeyExchange)m2).preMaster;
|
||||||
break;
|
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_RSA:
|
||||||
case K_DHE_DSS:
|
case K_DHE_DSS:
|
||||||
case K_DH_ANON:
|
case K_DH_ANON:
|
||||||
@ -1116,8 +1079,13 @@ final class ClientHandshaker extends Handshaker {
|
|||||||
preMasterSecret = ecdh.getAgreedSecret(serverKey);
|
preMasterSecret = ecdh.getAgreedSecret(serverKey);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IOException("Internal error: unknown key exchange "
|
if (ClientKeyExchangeService.find(keyExchange.name) != null) {
|
||||||
+ keyExchange);
|
preMasterSecret =
|
||||||
|
((ClientKeyExchange) m2).clientKeyExchange();
|
||||||
|
} else {
|
||||||
|
throw new IOException("Internal error: unknown key exchange "
|
||||||
|
+ keyExchange);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateKeys(preMasterSecret, null);
|
calculateKeys(preMasterSecret, null);
|
||||||
|
@ -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();
|
||||||
|
}
|
@ -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<String,ClientKeyExchangeService>
|
||||||
|
providers = new HashMap<>();
|
||||||
|
|
||||||
|
static {
|
||||||
|
final String key = "java.home";
|
||||||
|
String path = AccessController.doPrivileged(
|
||||||
|
new GetPropertyAction(key), null,
|
||||||
|
new PropertyPermission(key, "read"));
|
||||||
|
ServiceLoader<ClientKeyExchangeService> sc =
|
||||||
|
AccessController.doPrivileged(
|
||||||
|
(PrivilegedAction<ServiceLoader<ClientKeyExchangeService>>)
|
||||||
|
() -> ServiceLoader.loadInstalled(ClientKeyExchangeService.class),
|
||||||
|
null,
|
||||||
|
new FilePermission(new File(path, "-").toString(), "read"));
|
||||||
|
Iterator<ClientKeyExchangeService> 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;
|
||||||
|
}
|
@ -64,24 +64,9 @@ final class JsseJce {
|
|||||||
// If true, then all the Kerberos-based crypto we need is available.
|
// If true, then all the Kerberos-based crypto we need is available.
|
||||||
private final static boolean kerberosAvailable;
|
private final static boolean kerberosAvailable;
|
||||||
static {
|
static {
|
||||||
boolean temp;
|
ClientKeyExchangeService p =
|
||||||
try {
|
ClientKeyExchangeService.find("KRB5");
|
||||||
AccessController.doPrivileged(
|
kerberosAvailable = (p != null);
|
||||||
new PrivilegedExceptionAction<Void>() {
|
|
||||||
@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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
@ -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<Class<?>>() {
|
|
||||||
@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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<Krb5Proxy>() {
|
|
||||||
@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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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);
|
|
||||||
}
|
|
@ -352,18 +352,13 @@ final class SSLAlgorithmConstraints implements AlgorithmConstraints {
|
|||||||
components.add("ECDH_ANON");
|
components.add("ECDH_ANON");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case K_KRB5:
|
|
||||||
if (!forCertPathOnly) {
|
|
||||||
components.add("KRB5");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case K_KRB5_EXPORT:
|
|
||||||
if (!forCertPathOnly) {
|
|
||||||
components.add("KRB5_EXPORT");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
// ignore
|
if (ClientKeyExchangeService.find(keyExchange.name) != null) {
|
||||||
|
if (!forCertPathOnly) {
|
||||||
|
components.add(keyExchange.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// otherwise ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
return components;
|
return components;
|
||||||
|
@ -425,10 +425,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
// change record of peer identity even by accident, much
|
// change record of peer identity even by accident, much
|
||||||
// less do it intentionally.
|
// less do it intentionally.
|
||||||
//
|
//
|
||||||
if ((cipherSuite.keyExchange == K_KRB5) ||
|
if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
|
||||||
(cipherSuite.keyExchange == K_KRB5_EXPORT)) {
|
|
||||||
throw new SSLPeerUnverifiedException("no certificates expected"
|
throw new SSLPeerUnverifiedException("no certificates expected"
|
||||||
+ " for Kerberos cipher suites");
|
+ " for " + cipherSuite.keyExchange + " cipher suites");
|
||||||
}
|
}
|
||||||
if (peerCerts == null) {
|
if (peerCerts == null) {
|
||||||
throw new SSLPeerUnverifiedException("peer not authenticated");
|
throw new SSLPeerUnverifiedException("peer not authenticated");
|
||||||
@ -481,10 +480,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
// change record of peer identity even by accident, much
|
// change record of peer identity even by accident, much
|
||||||
// less do it intentionally.
|
// less do it intentionally.
|
||||||
//
|
//
|
||||||
if ((cipherSuite.keyExchange == K_KRB5) ||
|
if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
|
||||||
(cipherSuite.keyExchange == K_KRB5_EXPORT)) {
|
|
||||||
throw new SSLPeerUnverifiedException("no certificates expected"
|
throw new SSLPeerUnverifiedException("no certificates expected"
|
||||||
+ " for Kerberos cipher suites");
|
+ " for " + cipherSuite.keyExchange + " cipher suites");
|
||||||
}
|
}
|
||||||
if (peerCerts == null) {
|
if (peerCerts == null) {
|
||||||
throw new SSLPeerUnverifiedException("peer not authenticated");
|
throw new SSLPeerUnverifiedException("peer not authenticated");
|
||||||
@ -522,10 +520,9 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
* change record of peer identity even by accident, much
|
* change record of peer identity even by accident, much
|
||||||
* less do it intentionally.
|
* less do it intentionally.
|
||||||
*/
|
*/
|
||||||
if ((cipherSuite.keyExchange == K_KRB5) ||
|
if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
|
||||||
(cipherSuite.keyExchange == K_KRB5_EXPORT)) {
|
|
||||||
throw new SSLPeerUnverifiedException("no certificates expected"
|
throw new SSLPeerUnverifiedException("no certificates expected"
|
||||||
+ " for Kerberos cipher suites");
|
+ " for " + cipherSuite.keyExchange + " cipher suites");
|
||||||
}
|
}
|
||||||
if (peerCerts != null) {
|
if (peerCerts != null) {
|
||||||
return peerCerts.clone();
|
return peerCerts.clone();
|
||||||
@ -540,7 +537,7 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
*
|
*
|
||||||
* @return the peer's principal. Returns an X500Principal of the
|
* @return the peer's principal. Returns an X500Principal of the
|
||||||
* end-entity certificate for X509-based cipher suites, and
|
* 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
|
* @throws SSLPeerUnverifiedException if the peer's identity has not
|
||||||
* been verified
|
* been verified
|
||||||
@ -549,12 +546,10 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
public Principal getPeerPrincipal()
|
public Principal getPeerPrincipal()
|
||||||
throws SSLPeerUnverifiedException
|
throws SSLPeerUnverifiedException
|
||||||
{
|
{
|
||||||
if ((cipherSuite.keyExchange == K_KRB5) ||
|
if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
|
||||||
(cipherSuite.keyExchange == K_KRB5_EXPORT)) {
|
|
||||||
if (peerPrincipal == null) {
|
if (peerPrincipal == null) {
|
||||||
throw new SSLPeerUnverifiedException("peer not authenticated");
|
throw new SSLPeerUnverifiedException("peer not authenticated");
|
||||||
} else {
|
} else {
|
||||||
// Eliminate dependency on KerberosPrincipal
|
|
||||||
return peerPrincipal;
|
return peerPrincipal;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -569,15 +564,13 @@ final class SSLSessionImpl extends ExtendedSSLSession {
|
|||||||
*
|
*
|
||||||
* @return the principal sent to the peer. Returns an X500Principal
|
* @return the principal sent to the peer. Returns an X500Principal
|
||||||
* of the end-entity certificate for X509-based cipher suites, and
|
* 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.
|
* sent, then null is returned.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Principal getLocalPrincipal() {
|
public Principal getLocalPrincipal() {
|
||||||
|
|
||||||
if ((cipherSuite.keyExchange == K_KRB5) ||
|
if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
|
||||||
(cipherSuite.keyExchange == K_KRB5_EXPORT)) {
|
|
||||||
// Eliminate dependency on KerberosPrincipal
|
|
||||||
return (localPrincipal == null ? null : localPrincipal);
|
return (localPrincipal == null ? null : localPrincipal);
|
||||||
}
|
}
|
||||||
return (localCerts == null ? null :
|
return (localCerts == null ? null :
|
||||||
|
@ -38,8 +38,6 @@ import javax.crypto.spec.SecretKeySpec;
|
|||||||
|
|
||||||
import javax.net.ssl.*;
|
import javax.net.ssl.*;
|
||||||
|
|
||||||
import javax.security.auth.Subject;
|
|
||||||
|
|
||||||
import sun.security.util.KeyUtil;
|
import sun.security.util.KeyUtil;
|
||||||
import sun.security.action.GetPropertyAction;
|
import sun.security.action.GetPropertyAction;
|
||||||
import sun.security.ssl.HandshakeMessage.*;
|
import sun.security.ssl.HandshakeMessage.*;
|
||||||
@ -237,18 +235,6 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
handshakeState.update(pms, resumingSession);
|
handshakeState.update(pms, resumingSession);
|
||||||
preMasterSecret = this.clientKeyExchange(pms);
|
preMasterSecret = this.clientKeyExchange(pms);
|
||||||
break;
|
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_RSA:
|
||||||
case K_DHE_DSS:
|
case K_DHE_DSS:
|
||||||
case K_DH_ANON:
|
case K_DH_ANON:
|
||||||
@ -273,8 +259,24 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
preMasterSecret = this.clientKeyExchange(ecdhcke);
|
preMasterSecret = this.clientKeyExchange(ecdhcke);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new SSLProtocolException
|
ClientKeyExchangeService p =
|
||||||
("Unrecognized key exchange: " + keyExchange);
|
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
|
// validate subject identity
|
||||||
if (resumingSession) {
|
if (resumingSession) {
|
||||||
CipherSuite suite = previous.getSuite();
|
CipherSuite suite = previous.getSuite();
|
||||||
if (suite.keyExchange == K_KRB5 ||
|
ClientKeyExchangeService p =
|
||||||
suite.keyExchange == K_KRB5_EXPORT) {
|
ClientKeyExchangeService.find(suite.keyExchange.name);
|
||||||
|
if (p != null) {
|
||||||
Principal localPrincipal = previous.getLocalPrincipal();
|
Principal localPrincipal = previous.getLocalPrincipal();
|
||||||
|
|
||||||
Subject subject = null;
|
if (p.isRelated(
|
||||||
try {
|
false, getAccSE(), localPrincipal)) {
|
||||||
subject = AccessController.doPrivileged(
|
if (debug != null && Debug.isOn("session"))
|
||||||
new PrivilegedExceptionAction<Subject>() {
|
System.out.println("Subject can" +
|
||||||
@Override
|
" provide creds for princ");
|
||||||
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");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
resumingSession = false;
|
resumingSession = false;
|
||||||
if (debug != null && Debug.isOn("session"))
|
if (debug != null && Debug.isOn("session"))
|
||||||
System.out.println("Kerberos credentials are" +
|
System.out.println("Subject cannot" +
|
||||||
" not present in the current Subject;" +
|
" provide creds for princ");
|
||||||
" check if " +
|
|
||||||
" javax.security.auth.useSubjectAsCreds" +
|
|
||||||
" system property has been set to false");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -871,9 +847,8 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
* defined in the protocol spec are explicitly stated to require
|
* defined in the protocol spec are explicitly stated to require
|
||||||
* using RSA certificates.
|
* using RSA certificates.
|
||||||
*/
|
*/
|
||||||
if (keyExchange == K_KRB5 || keyExchange == K_KRB5_EXPORT) {
|
if (ClientKeyExchangeService.find(cipherSuite.keyExchange.name) != null) {
|
||||||
// Server certificates are omitted for Kerberos ciphers
|
// No external key exchange provider needs a cert now.
|
||||||
|
|
||||||
} else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) {
|
} else if ((keyExchange != K_DH_ANON) && (keyExchange != K_ECDH_ANON)) {
|
||||||
if (certs == null) {
|
if (certs == null) {
|
||||||
throw new RuntimeException("no certificates");
|
throw new RuntimeException("no certificates");
|
||||||
@ -915,9 +890,7 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
ServerKeyExchange m3;
|
ServerKeyExchange m3;
|
||||||
switch (keyExchange) {
|
switch (keyExchange) {
|
||||||
case K_RSA:
|
case K_RSA:
|
||||||
case K_KRB5:
|
// no server key exchange for RSA ciphersuites
|
||||||
case K_KRB5_EXPORT:
|
|
||||||
// no server key exchange for RSA or KRB5 ciphersuites
|
|
||||||
m3 = null;
|
m3 = null;
|
||||||
break;
|
break;
|
||||||
case K_RSA_EXPORT:
|
case K_RSA_EXPORT:
|
||||||
@ -980,6 +953,13 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
m3 = null;
|
m3 = null;
|
||||||
break;
|
break;
|
||||||
default:
|
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);
|
throw new RuntimeException("internal error: " + keyExchange);
|
||||||
}
|
}
|
||||||
if (m3 != null) {
|
if (m3 != null) {
|
||||||
@ -1000,10 +980,10 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
// Needed only if server requires client to authenticate self.
|
// Needed only if server requires client to authenticate self.
|
||||||
// Illegal for anonymous flavors, so we need to check that.
|
// 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 &&
|
if (doClientAuth != ClientAuthType.CLIENT_AUTH_NONE &&
|
||||||
keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
|
keyExchange != K_DH_ANON && keyExchange != K_ECDH_ANON &&
|
||||||
keyExchange != K_KRB5 && keyExchange != K_KRB5_EXPORT) {
|
ClientKeyExchangeService.find(keyExchange.name) == null) {
|
||||||
|
|
||||||
CertificateRequest m4;
|
CertificateRequest m4;
|
||||||
X509Certificate caCerts[];
|
X509Certificate caCerts[];
|
||||||
@ -1313,13 +1293,6 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
}
|
}
|
||||||
setupStaticECDHKeys();
|
setupStaticECDHKeys();
|
||||||
break;
|
break;
|
||||||
case K_KRB5:
|
|
||||||
case K_KRB5_EXPORT:
|
|
||||||
// need Kerberos Key
|
|
||||||
if (!setupKerberosKeys()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case K_DH_ANON:
|
case K_DH_ANON:
|
||||||
// no certs needed for anonymous
|
// no certs needed for anonymous
|
||||||
setupEphemeralDHKeys(suite.exportable, null);
|
setupEphemeralDHKeys(suite.exportable, null);
|
||||||
@ -1331,8 +1304,26 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
// internal error, unknown key exchange
|
ClientKeyExchangeService p =
|
||||||
throw new RuntimeException("Unrecognized cipherSuite: " + suite);
|
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);
|
setCipherSuite(suite);
|
||||||
|
|
||||||
@ -1522,73 +1513,10 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
return true;
|
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<Object>() {
|
|
||||||
@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
|
* Returns premaster secret for external key exchange services.
|
||||||
* the session key. See RFC 2712.
|
|
||||||
*/
|
*/
|
||||||
private SecretKey clientKeyExchange(KerberosClientKeyExchange mesg)
|
private SecretKey clientKeyExchange(ClientKeyExchange mesg)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
if (debug != null && Debug.isOn("handshake")) {
|
if (debug != null && Debug.isOn("handshake")) {
|
||||||
@ -1599,8 +1527,7 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
session.setPeerPrincipal(mesg.getPeerPrincipal());
|
session.setPeerPrincipal(mesg.getPeerPrincipal());
|
||||||
session.setLocalPrincipal(mesg.getLocalPrincipal());
|
session.setLocalPrincipal(mesg.getLocalPrincipal());
|
||||||
|
|
||||||
byte[] b = mesg.getUnencryptedPreMasterSecret();
|
return mesg.clientKeyExchange();
|
||||||
return new SecretKeySpec(b, "TlsPremasterSecret");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1705,7 +1632,7 @@ final class ServerHandshaker extends Handshaker {
|
|||||||
*/
|
*/
|
||||||
if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
|
if (doClientAuth == ClientAuthType.CLIENT_AUTH_REQUIRED) {
|
||||||
// get X500Principal of the end-entity certificate for X509-based
|
// 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();
|
session.getPeerPrincipal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ import java.security.cert.*;
|
|||||||
|
|
||||||
import javax.security.auth.x500.X500Principal;
|
import javax.security.auth.x500.X500Principal;
|
||||||
|
|
||||||
import sun.security.ssl.Krb5Helper;
|
import sun.security.ssl.ClientKeyExchangeService;
|
||||||
import sun.security.x509.X500Name;
|
import sun.security.x509.X500Name;
|
||||||
|
|
||||||
import sun.net.util.IPAddressUtil;
|
import sun.net.util.IPAddressUtil;
|
||||||
@ -108,7 +108,12 @@ public class HostnameChecker {
|
|||||||
* Return the Server name from Kerberos principal.
|
* Return the Server name from Kerberos principal.
|
||||||
*/
|
*/
|
||||||
public static String getServerName(Principal 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -0,0 +1,2 @@
|
|||||||
|
sun.security.krb5.internal.ssl.Krb5KeyExchangeService
|
||||||
|
|
@ -23,7 +23,7 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package sun.security.ssl.krb5;
|
package sun.security.krb5.internal.ssl;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.security.*;
|
import java.security.*;
|
||||||
@ -68,13 +68,13 @@ final class KerberosPreMasterSecret {
|
|||||||
* @param sessionKey Kerberos session key for encrypting premaster secret
|
* @param sessionKey Kerberos session key for encrypting premaster secret
|
||||||
*/
|
*/
|
||||||
KerberosPreMasterSecret(ProtocolVersion protocolVersion,
|
KerberosPreMasterSecret(ProtocolVersion protocolVersion,
|
||||||
SecureRandom generator, EncryptionKey sessionKey) throws IOException {
|
SecureRandom generator, EncryptionKey sessionKey) throws IOException {
|
||||||
|
|
||||||
if (sessionKey.getEType() ==
|
if (sessionKey.getEType() ==
|
||||||
EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
|
EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
"session keys with des3-cbc-hmac-sha1-kd encryption type " +
|
"session keys with des3-cbc-hmac-sha1-kd encryption type " +
|
||||||
"are not supported for TLS Kerberos cipher suites");
|
"are not supported for TLS Kerberos cipher suites");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.protocolVersion = protocolVersion;
|
this.protocolVersion = protocolVersion;
|
||||||
@ -83,12 +83,12 @@ final class KerberosPreMasterSecret {
|
|||||||
// Encrypt premaster secret
|
// Encrypt premaster secret
|
||||||
try {
|
try {
|
||||||
EncryptedData eData = new EncryptedData(sessionKey, preMaster,
|
EncryptedData eData = new EncryptedData(sessionKey, preMaster,
|
||||||
KeyUsage.KU_UNKNOWN);
|
KeyUsage.KU_UNKNOWN);
|
||||||
encrypted = eData.getBytes(); // not ASN.1 encoded.
|
encrypted = eData.getBytes(); // not ASN.1 encoded.
|
||||||
|
|
||||||
} catch (KrbException e) {
|
} catch (KrbException e) {
|
||||||
throw (SSLKeyException)new SSLKeyException
|
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
|
* @param sessionKey Kerberos session key to be used for decryption
|
||||||
*/
|
*/
|
||||||
KerberosPreMasterSecret(ProtocolVersion currentVersion,
|
KerberosPreMasterSecret(ProtocolVersion currentVersion,
|
||||||
ProtocolVersion clientVersion,
|
ProtocolVersion clientVersion,
|
||||||
SecureRandom generator, HandshakeInStream input,
|
SecureRandom generator, byte[] encrypted,
|
||||||
EncryptionKey sessionKey) throws IOException {
|
EncryptionKey sessionKey) throws IOException {
|
||||||
|
|
||||||
// Extract encrypted premaster secret from message
|
if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
|
||||||
encrypted = input.getBytes16();
|
|
||||||
|
|
||||||
if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
|
|
||||||
if (encrypted != null) {
|
if (encrypted != null) {
|
||||||
Debug.println(System.out,
|
Debug.println(System.out,
|
||||||
"encrypted premaster secret", encrypted);
|
"encrypted premaster secret", encrypted);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sessionKey.getEType() ==
|
if (sessionKey.getEType() ==
|
||||||
EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
|
EncryptedData.ETYPE_DES3_CBC_HMAC_SHA1_KD) {
|
||||||
throw new IOException(
|
throw new IOException(
|
||||||
"session keys with des3-cbc-hmac-sha1-kd encryption type " +
|
"session keys with des3-cbc-hmac-sha1-kd encryption type " +
|
||||||
"are not supported for TLS Kerberos cipher suites");
|
"are not supported for TLS Kerberos cipher suites");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Decrypt premaster secret
|
// Decrypt premaster secret
|
||||||
try {
|
try {
|
||||||
EncryptedData data = new EncryptedData(sessionKey.getEType(),
|
EncryptedData data = new EncryptedData(sessionKey.getEType(),
|
||||||
null /* optional kvno */, encrypted);
|
null /* optional kvno */, encrypted);
|
||||||
|
|
||||||
byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
|
byte[] temp = data.decrypt(sessionKey, KeyUsage.KU_UNKNOWN);
|
||||||
if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
|
if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
|
||||||
if (encrypted != null) {
|
if (encrypted != null) {
|
||||||
Debug.println(System.out,
|
Debug.println(System.out,
|
||||||
"decrypted premaster secret", temp);
|
"decrypted premaster secret", temp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove padding bytes after decryption. Only DES and DES3 have
|
// Remove padding bytes after decryption. Only DES and DES3 have
|
||||||
@ -162,9 +159,9 @@ final class KerberosPreMasterSecret {
|
|||||||
preMaster = temp;
|
preMaster = temp;
|
||||||
|
|
||||||
protocolVersion = ProtocolVersion.valueOf(preMaster[0],
|
protocolVersion = ProtocolVersion.valueOf(preMaster[0],
|
||||||
preMaster[1]);
|
preMaster[1]);
|
||||||
if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
|
if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
|
||||||
System.out.println("Kerberos PreMasterSecret version: "
|
System.out.println("Kerberos PreMasterSecret version: "
|
||||||
+ protocolVersion);
|
+ protocolVersion);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -202,11 +199,11 @@ final class KerberosPreMasterSecret {
|
|||||||
* that will prevent an attacking client from differentiating
|
* that will prevent an attacking client from differentiating
|
||||||
* different kinds of decrypted ClientKeyExchange bogosities.
|
* different kinds of decrypted ClientKeyExchange bogosities.
|
||||||
*/
|
*/
|
||||||
if ((preMaster == null) || (preMaster.length != 48)
|
if ((preMaster == null) || (preMaster.length != 48)
|
||||||
|| versionMismatch) {
|
|| versionMismatch) {
|
||||||
if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
|
if (HandshakeMessage.debug != null && Debug.isOn("handshake")) {
|
||||||
System.out.println("Kerberos PreMasterSecret error, "
|
System.out.println("Kerberos PreMasterSecret error, "
|
||||||
+ "generating random secret");
|
+ "generating random secret");
|
||||||
if (preMaster != null) {
|
if (preMaster != null) {
|
||||||
Debug.println(System.out, "Invalid secret", preMaster);
|
Debug.println(System.out, "Invalid secret", preMaster);
|
||||||
}
|
}
|
||||||
@ -243,14 +240,14 @@ final class KerberosPreMasterSecret {
|
|||||||
* @param generator random number generator to use for generating secret.
|
* @param generator random number generator to use for generating secret.
|
||||||
*/
|
*/
|
||||||
KerberosPreMasterSecret(ProtocolVersion protocolVersion,
|
KerberosPreMasterSecret(ProtocolVersion protocolVersion,
|
||||||
SecureRandom generator) {
|
SecureRandom generator) {
|
||||||
|
|
||||||
this.protocolVersion = protocolVersion;
|
this.protocolVersion = protocolVersion;
|
||||||
preMaster = generatePreMaster(generator, protocolVersion);
|
preMaster = generatePreMaster(generator, protocolVersion);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] generatePreMaster(SecureRandom rand,
|
private static byte[] generatePreMaster(SecureRandom rand,
|
||||||
ProtocolVersion ver) {
|
ProtocolVersion ver) {
|
||||||
|
|
||||||
byte[] pm = new byte[48];
|
byte[] pm = new byte[48];
|
||||||
rand.nextBytes(pm);
|
rand.nextBytes(pm);
|
@ -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<ServiceCreds>)
|
||||||
|
() -> 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<Subject>)
|
||||||
|
() -> 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<Principal> 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<KerberosKey[]>() {
|
||||||
|
@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<String>() {
|
||||||
|
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<KerberosTicket>() {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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<KerberosKey[]>() {
|
|
||||||
@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<String>() {
|
|
||||||
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<KerberosTicket>() {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -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<Principal> 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;
|
|
||||||
}
|
|
||||||
}
|
|
229
jdk/test/sun/security/krb5/auto/SSLwithPerms.java
Normal file
229
jdk/test/sun/security/krb5/auto/SSLwithPerms.java
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user