提交 3a370799 编写于 作者: W weijun

8005523: Unbound krb5 for TLS

Reviewed-by: xuelei
上级 f6dea072
......@@ -86,10 +86,10 @@ public class KerberosClientKeyExchange extends HandshakeMessage {
public KerberosClientKeyExchange(ProtocolVersion protocolVersion,
ProtocolVersion clientVersion, SecureRandom rand,
HandshakeInStream input, SecretKey[] serverKeys) throws IOException {
HandshakeInStream input, AccessControlContext acc, Object serverKeys) throws IOException {
if (impl != null) {
init(protocolVersion, clientVersion, rand, input, serverKeys);
init(protocolVersion, clientVersion, rand, input, acc, serverKeys);
} else {
throw new IllegalStateException("Kerberos is unavailable");
}
......@@ -126,10 +126,10 @@ public class KerberosClientKeyExchange extends HandshakeMessage {
public void init(ProtocolVersion protocolVersion,
ProtocolVersion clientVersion, SecureRandom rand,
HandshakeInStream input, SecretKey[] serverKeys) throws IOException {
HandshakeInStream input, AccessControlContext acc, Object ServiceCreds) throws IOException {
if (impl != null) {
impl.init(protocolVersion, clientVersion, rand, input, serverKeys);
impl.init(protocolVersion, clientVersion, rand, input, acc, ServiceCreds);
}
}
......
......@@ -94,18 +94,18 @@ public final class Krb5Helper {
/**
* Returns the KerberosKeys for the default server-side principal.
*/
public static SecretKey[] getServerKeys(AccessControlContext acc)
public static Object getServiceCreds(AccessControlContext acc)
throws LoginException {
ensureAvailable();
return proxy.getServerKeys(acc);
return proxy.getServiceCreds(acc);
}
/**
* Returns the server-side principal name associated with the KerberosKey.
*/
public static String getServerPrincipalName(SecretKey kerberosKey) {
public static String getServerPrincipalName(Object serviceCreds) {
ensureAvailable();
return proxy.getServerPrincipalName(kerberosKey);
return proxy.getServerPrincipalName(serviceCreds);
}
/**
......@@ -124,4 +124,12 @@ public final class Krb5Helper {
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);
}
}
......@@ -50,14 +50,14 @@ public interface Krb5Proxy {
/**
* Returns the KerberosKeys for the default server-side principal.
* Returns the Kerberos ServiceCreds for the default server-side principal.
*/
SecretKey[] getServerKeys(AccessControlContext acc) throws LoginException;
Object getServiceCreds(AccessControlContext acc) throws LoginException;
/**
* Returns the server-side principal name associated with the KerberosKey.
*/
String getServerPrincipalName(SecretKey kerberosKey);
String getServerPrincipalName(Object serviceCreds);
/**
* Returns the hostname embedded in the principal name.
......@@ -68,4 +68,9 @@ public interface Krb5Proxy {
* 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);
}
......@@ -62,7 +62,7 @@ final class ServerHandshaker extends Handshaker {
private X509Certificate[] certs;
private PrivateKey privateKey;
private SecretKey[] kerberosKeys;
private Object serviceCreds;
// flag to check for clientCertificateVerify message
private boolean needClientVerify = false;
......@@ -200,7 +200,8 @@ final class ServerHandshaker extends Handshaker {
clientRequestedVersion,
sslContext.getSecureRandom(),
input,
kerberosKeys));
this.getAccSE(),
serviceCreds));
break;
case K_DHE_RSA:
case K_DHE_DSS:
......@@ -543,18 +544,15 @@ final class ServerHandshaker extends Handshaker {
if (subject != null) {
// Eliminate dependency on KerberosPrincipal
Set<Principal> principals =
subject.getPrincipals(Principal.class);
if (!principals.contains(localPrincipal)) {
resumingSession = false;
if (debug != null && Debug.isOn("session")) {
System.out.println("Subject identity" +
" is not the same");
}
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 identity" +
" is same");
System.out.println("Subject cannot" +
" provide creds for princ");
}
} else {
resumingSession = false;
......@@ -1316,49 +1314,51 @@ final class ServerHandshaker extends Handshaker {
* @return true if successful, false if not available or invalid
*/
private boolean setupKerberosKeys() {
if (kerberosKeys != null) {
if (serviceCreds != null) {
return true;
}
try {
final AccessControlContext acc = getAccSE();
kerberosKeys = AccessController.doPrivileged(
serviceCreds = AccessController.doPrivileged(
// Eliminate dependency on KerberosKey
new PrivilegedExceptionAction<SecretKey[]>() {
new PrivilegedExceptionAction<Object>() {
@Override
public SecretKey[] run() throws Exception {
public Object run() throws Exception {
// get kerberos key for the default principal
return Krb5Helper.getServerKeys(acc);
return Krb5Helper.getServiceCreds(acc);
}});
// check permission to access and use the secret key of the
// Kerberized "host" service
if (kerberosKeys != null && kerberosKeys.length > 0) {
if (serviceCreds != null) {
if (debug != null && Debug.isOn("handshake")) {
for (SecretKey k: kerberosKeys) {
System.out.println("Using Kerberos key: " +
k);
}
System.out.println("Using Kerberos creds");
}
String serverPrincipal =
Krb5Helper.getServerPrincipalName(kerberosKeys[0]);
SecurityManager sm = System.getSecurityManager();
try {
if (sm != null) {
// Eliminate dependency on ServicePermission
sm.checkPermission(Krb5Helper.getServicePermission(
serverPrincipal, "accept"), acc);
}
} catch (SecurityException se) {
kerberosKeys = null;
// %%% destroy keys? or will that affect Subject?
if (debug != null && Debug.isOn("handshake"))
System.out.println("Permission to access Kerberos"
+ " secret key denied");
return false;
Krb5Helper.getServerPrincipalName(serviceCreds);
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(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 (kerberosKeys != null && kerberosKeys.length > 0);
return serviceCreds != null;
} catch (PrivilegedActionException e) {
// Likely exception here is LoginExceptin
if (debug != null && Debug.isOn("handshake")) {
......
......@@ -33,8 +33,8 @@ import java.security.PrivilegedExceptionAction;
import java.security.PrivilegedActionException;
import java.security.SecureRandom;
import java.net.InetAddress;
import java.security.PrivilegedAction;
import javax.crypto.SecretKey;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.kerberos.KerberosKey;
import javax.security.auth.kerberos.KerberosPrincipal;
......@@ -44,18 +44,19 @@ import sun.security.jgss.GSSCaller;
import sun.security.krb5.EncryptionKey;
import sun.security.krb5.EncryptedData;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.Realm;
import sun.security.krb5.internal.Ticket;
import sun.security.krb5.internal.EncTicketPart;
import sun.security.krb5.internal.crypto.KeyUsage;
import sun.security.jgss.krb5.Krb5Util;
import sun.security.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;
/**
......@@ -138,16 +139,15 @@ public final class KerberosClientKeyExchangeImpl
* @param rand random number generator used for generating random
* premaster secret if ticket and/or premaster verification fails
* @param input inputstream from which to get ASN.1-encoded KerberosWrapper
* @param serverKey server's master secret key
* @param acc the AccessControlContext of the handshaker
* @param serviceCreds server's creds
*/
@Override
public void init(ProtocolVersion protocolVersion,
ProtocolVersion clientVersion,
SecureRandom rand, HandshakeInStream input, SecretKey[] secretKeys)
SecureRandom rand, HandshakeInStream input, AccessControlContext acc, Object serviceCreds)
throws IOException {
KerberosKey[] serverKeys = (KerberosKey[])secretKeys;
// Read ticket
encodedTicket = input.getBytes16();
......@@ -163,9 +163,42 @@ public final class KerberosClientKeyExchangeImpl
EncryptedData encPart = t.encPart;
PrincipalName ticketSname = t.sname;
Realm ticketRealm = t.sname.getRealm();
String serverPrincipal = serverKeys[0].getPrincipal().getName();
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
......@@ -174,17 +207,6 @@ public final class KerberosClientKeyExchangeImpl
* before promising the client
*/
// Check that ticket Sname matches serverPrincipal
String ticketPrinc = ticketSname.toString();
if (!ticketPrinc.equals(serverPrincipal)) {
if (debug != null && Debug.isOn("handshake"))
System.out.println("Service principal in Ticket does not"
+ " match associated principal in KerberosKey");
throw new IOException("Server principal is " +
serverPrincipal + " but ticket is for " +
ticketPrinc);
}
// See if we have the right key to decrypt the ticket to get
// the session key.
int encPartKeyType = encPart.getEType();
......@@ -198,9 +220,8 @@ public final class KerberosClientKeyExchangeImpl
}
if (dkey == null) {
// %%% Should print string repr of etype
throw new IOException(
"Cannot find key of appropriate type to decrypt ticket - need etype " +
encPartKeyType);
throw new IOException("Cannot find key of appropriate type" +
" to decrypt ticket - need etype " + encPartKeyType);
}
EncryptionKey secretKey = new EncryptionKey(
......@@ -222,7 +243,7 @@ public final class KerberosClientKeyExchangeImpl
sessionKey = encTicketPart.key;
if (debug != null && Debug.isOn("handshake")) {
System.out.println("server principal: " + serverPrincipal);
System.out.println("server principal: " + ticketSname);
System.out.println("cname: " + encTicketPart.cname.toString());
}
} catch (IOException e) {
......@@ -382,12 +403,22 @@ public final class KerberosClientKeyExchangeImpl
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, keys[i].getVersionNumber())) {
if (versionMatches(version, kv)) {
return keys[i];
} else if (kv > kvno_found) {
key_found = keys[i];
kvno_found = kv;
}
}
}
......@@ -399,18 +430,25 @@ public final class KerberosClientKeyExchangeImpl
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, keys[i].getVersionNumber())) {
if (versionMatches(version, kv)) {
return new KerberosKey(keys[i].getPrincipal(),
keys[i].getEncoded(),
etype,
keys[i].getVersionNumber());
kv);
} else if (kv > kvno_found) {
key_found = new KerberosKey(keys[i].getPrincipal(),
keys[i].getEncoded(),
etype,
kv);
kvno_found = kv;
}
}
}
}
if (etypeFound) {
throw new KrbException(Krb5.KRB_AP_ERR_BADKEYVER);
return key_found;
}
return null;
}
......
......@@ -28,9 +28,11 @@ 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;
......@@ -61,17 +63,16 @@ public class Krb5ProxyImpl implements Krb5Proxy {
}
@Override
public SecretKey[] getServerKeys(AccessControlContext acc)
public Object getServiceCreds(AccessControlContext acc)
throws LoginException {
ServiceCreds serviceCreds =
Krb5Util.getServiceCreds(GSSCaller.CALLER_SSL_SERVER, null, acc);
return serviceCreds != null ? serviceCreds.getKKeys() :
new KerberosKey[0];
return serviceCreds;
}
@Override
public String getServerPrincipalName(SecretKey kerberosKey) {
return ((KerberosKey)kerberosKey).getPrincipal().getName();
public String getServerPrincipalName(Object serviceCreds) {
return ((ServiceCreds)serviceCreds).getName();
}
@Override
......@@ -100,4 +101,21 @@ public class Krb5ProxyImpl implements Krb5Proxy {
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;
}
}
......@@ -23,10 +23,11 @@
/*
* @test
* @bug 6894643 6913636
* @bug 6894643 6913636 8005523
* @summary Test JSSE Kerberos ciphersuite
* @run main/othervm SSL TLS_KRB5_WITH_RC4_128_SHA
* @run main/othervm SSL TLS_KRB5_WITH_RC4_128_MD5
* @run main/othervm SSL TLS_KRB5_WITH_RC4_128_SHA unbound
* @run main/othervm SSL TLS_KRB5_WITH_3DES_EDE_CBC_SHA
* @run main/othervm SSL TLS_KRB5_WITH_3DES_EDE_CBC_MD5
* @run main/othervm SSL TLS_KRB5_WITH_DES_CBC_SHA
......@@ -38,14 +39,17 @@
*/
import java.io.*;
import java.net.InetAddress;
import java.security.AccessControlException;
import java.security.Permission;
import javax.net.ssl.*;
import java.security.Principal;
import java.util.Date;
import javax.security.auth.kerberos.ServicePermission;
import sun.security.jgss.GSSUtil;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.internal.ktab.KeyTab;
public class SSL {
public class SSL extends SecurityManager {
private static String krb5Cipher;
private static final int LOOP_LIMIT = 3;
......@@ -53,13 +57,32 @@ public class SSL {
private static volatile String server;
private static volatile int port;
private static String permChecks = "";
// 0-Not started, 1-Start OK, 2-Failure
private static volatile int serverState = 0;
@Override
public void checkPermission(Permission perm, Object context) {
checkPermission(perm);
}
public void checkPermission(Permission perm) {
if (!(perm instanceof ServicePermission)) {
return;
}
ServicePermission p = (ServicePermission)perm;
permChecks = permChecks + p.getActions().toUpperCase().charAt(0);
}
public static void main(String[] args) throws Exception {
krb5Cipher = args[0];
boolean unbound = args.length > 1;
System.setSecurityManager(new SSL());
KDC kdc = KDC.create(OneKDC.REALM);
// Run this after KDC, so our own DNS service can be started
try {
......@@ -85,6 +108,7 @@ public class SSL {
// and use the middle one as the real key
kdc.addPrincipal("host/" + server, "pass2".toCharArray());
// JAAS config entry name ssl
System.setProperty("java.security.auth.login.config", OneKDC.JAAS_CONF);
File f = new File(OneKDC.JAAS_CONF);
......@@ -92,7 +116,9 @@ public class SSL {
fos.write((
"ssl {\n" +
" com.sun.security.auth.module.Krb5LoginModule required\n" +
" principal=\"host/" + server + "\"\n" +
(unbound ?
" principal=*\n" :
" principal=\"host/" + server + "\"\n") +
" useKeyTab=true\n" +
" keyTab=" + OneKDC.KTAB + "\n" +
" isInitiator=false\n" +
......@@ -103,7 +129,6 @@ public class SSL {
Context c;
final Context s = Context.fromJAAS("ssl");
// There's no keytab file when server starts.
s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
Thread server = new Thread(new Runnable() {
......@@ -127,21 +152,6 @@ public class SSL {
throw new Exception("Server already failed");
}
// Now create the keytab
/*
// Add 3 versions of keys into keytab
KeyTab ktab = KeyTab.create(OneKDC.KTAB);
PrincipalName service = new PrincipalName(
"host/" + server, PrincipalName.KRB_NT_SRV_HST);
ktab.addEntry(service, "pass1".toCharArray(), 1);
ktab.addEntry(service, "pass2".toCharArray(), 2);
ktab.addEntry(service, "pass3".toCharArray(), 3);
ktab.save();
// and use the middle one as the real key
kdc.addPrincipal("host/" + server, "pass2".toCharArray());
*/
c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID);
c.doAs(new JsseClientAction(), null);
......@@ -157,20 +167,22 @@ public class SSL {
c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID);
c.doAs(new JsseClientAction(), null);
// Revoke the old key
/*Thread.sleep(2000);
ktab = KeyTab.create(OneKDC.KTAB);
ktab.addEntry(service, "pass5".toCharArray(), 5, false);
ktab.save();
c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
c.startAsClient("host/" + server, GSSUtil.GSS_KRB5_MECH_OID);
try {
c.doAs(new JsseClientAction(), null);
throw new Exception("Should fail this time.");
} catch (SSLException e) {
// Correct behavior.
}*/
// Permission checking check. Please note this is highly
// implementation related.
if (unbound) {
// For unbound, server does not know what name to check.
// Client checks "initiate", then server gets the name
// and checks "accept". Second connection resume.
if (!permChecks.equals("IA")) {
throw new Exception();
}
} else {
// For bound, JAAS checks "accept" once. Server checks again,
// client then checks "initiate". Second connection resume.
if (!permChecks.equals("AAI")) {
throw new Exception();
}
}
}
// Following codes copied from
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册