--- a/jdk/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java Fri Apr 26 16:09:53 2013 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/KerberosClientKeyExchange.java Sat Apr 27 18:25:16 2013 +0800
@@ -86,10 +86,10 @@
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 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);
}
}
--- a/jdk/src/share/classes/sun/security/ssl/Krb5Helper.java Fri Apr 26 16:09:53 2013 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/Krb5Helper.java Sat Apr 27 18:25:16 2013 +0800
@@ -94,18 +94,18 @@
/**
* 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 @@
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);
+ }
}
--- a/jdk/src/share/classes/sun/security/ssl/Krb5Proxy.java Fri Apr 26 16:09:53 2013 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/Krb5Proxy.java Sat Apr 27 18:25:16 2013 +0800
@@ -50,14 +50,14 @@
/**
- * 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 @@
* 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);
}
--- a/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java Fri Apr 26 16:09:53 2013 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/ServerHandshaker.java Sat Apr 27 18:25:16 2013 +0800
@@ -62,7 +62,7 @@
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 @@
clientRequestedVersion,
sslContext.getSecureRandom(),
input,
- kerberosKeys));
+ this.getAccSE(),
+ serviceCreds));
break;
case K_DHE_RSA:
case K_DHE_DSS:
@@ -543,18 +544,15 @@
if (subject != null) {
// Eliminate dependency on KerberosPrincipal
- Set<Principal> principals =
- subject.getPrincipals(Principal.class);
- if (!principals.contains(localPrincipal)) {
+ 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 not the same");
- }
- } else {
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 @@
* @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(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;
}
}
-
- 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;
- }
}
- return (kerberosKeys != null && kerberosKeys.length > 0);
+ return serviceCreds != null;
} catch (PrivilegedActionException e) {
// Likely exception here is LoginExceptin
if (debug != null && Debug.isOn("handshake")) {
--- a/jdk/src/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java Fri Apr 26 16:09:53 2013 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/krb5/KerberosClientKeyExchangeImpl.java Sat Apr 27 18:25:16 2013 +0800
@@ -33,8 +33,8 @@
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.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 @@
* @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 @@
EncryptedData encPart = t.encPart;
PrincipalName ticketSname = t.sname;
- Realm ticketRealm = t.sname.getRealm();
+
+ final ServiceCreds creds = (ServiceCreds)serviceCreds;
+ final KerberosPrincipal princ =
+ new KerberosPrincipal(ticketSname.toString());
- String serverPrincipal = serverKeys[0].getPrincipal().getName();
+ // 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 @@
* 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 @@
}
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 @@
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 @@
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 @@
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;
}
--- a/jdk/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java Fri Apr 26 16:09:53 2013 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/krb5/Krb5ProxyImpl.java Sat Apr 27 18:25:16 2013 +0800
@@ -28,9 +28,11 @@
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 @@
}
@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 @@
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;
+ }
}
--- a/jdk/test/sun/security/krb5/auto/SSL.java Fri Apr 26 16:09:53 2013 -0700
+++ b/jdk/test/sun/security/krb5/auto/SSL.java Sat Apr 27 18:25:16 2013 +0800
@@ -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 @@
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 @@
// 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 @@
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 @@
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 @@
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 @@
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