--- old/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java 2016-10-25 15:18:35.000000000 +0800 +++ new/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java 2016-10-25 15:18:35.000000000 +0800 @@ -301,12 +301,13 @@ if (!authenticator.ctime.inClockSkew()) throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW); + String alg = AuthTimeWithHash.DEFAULT_HASH_ALG; byte[] hash; try { - hash = MessageDigest.getInstance("MD5") + hash = MessageDigest.getInstance(AuthTimeWithHash.realAlg(alg)) .digest(apReqMessg.authenticator.cipher); } catch (NoSuchAlgorithmException ex) { - throw new AssertionError("Impossible"); + throw new AssertionError("Impossible " + alg); } char[] h = new char[hash.length * 2]; @@ -319,6 +320,7 @@ apReqMessg.ticket.sname.toString(), authenticator.ctime.getSeconds(), authenticator.cusec, + alg, new String(h)); rcache.checkAndStore(KerberosTime.now(), time); --- old/src/java.security.jgss/share/classes/sun/security/krb5/internal/rcache/AuthTime.java 2016-10-25 15:18:36.000000000 +0800 +++ new/src/java.security.jgss/share/classes/sun/security/krb5/internal/rcache/AuthTime.java 2016-10-25 15:18:36.000000000 +0800 @@ -116,14 +116,14 @@ if (st.countTokens() != 6) { throw new IOException("Incorrect rcache style"); } - st.nextToken(); + String hashAlg = st.nextToken(); String hash = st.nextToken(); st.nextToken(); client = st.nextToken(); st.nextToken(); server = st.nextToken(); return new AuthTimeWithHash( - client, server, ctime, cusec, hash); + client, server, ctime, cusec, hashAlg, hash); } else { return new AuthTime( client, server, ctime, cusec); --- old/src/java.security.jgss/share/classes/sun/security/krb5/internal/rcache/AuthTimeWithHash.java 2016-10-25 15:18:39.000000000 +0800 +++ new/src/java.security.jgss/share/classes/sun/security/krb5/internal/rcache/AuthTimeWithHash.java 2016-10-25 15:18:39.000000000 +0800 @@ -25,6 +25,8 @@ package sun.security.krb5.internal.rcache; +import sun.security.action.GetPropertyAction; + import java.util.Objects; /** @@ -34,14 +36,39 @@ public class AuthTimeWithHash extends AuthTime implements Comparable { + // The hash algorithm can be "HASH" or "SHA256". + public static final String DEFAULT_HASH_ALG; + + static { + if (GetPropertyAction.privilegedGetProperty( + "jdk.krb5.rcache.usemd5", "false").equals("true")) { + DEFAULT_HASH_ALG = "HASH"; + } else { + DEFAULT_HASH_ALG = "SHA256"; + } + } + + public static String realAlg(String alg) { + switch (alg) { + case "HASH": + return "MD5"; + case "SHA256": + return "SHA-256"; + default: + throw new AssertionError(alg + " is not HASH or SHA256"); + } + } + + final String hashAlg; final String hash; /** * Constructs a new AuthTimeWithHash. */ public AuthTimeWithHash(String client, String server, - int ctime, int cusec, String hash) { + int ctime, int cusec, String hashAlg, String hash) { super(client, server, ctime, cusec); + this.hashAlg = hashAlg; this.hash = hash; } @@ -56,6 +83,7 @@ if (!(o instanceof AuthTimeWithHash)) return false; AuthTimeWithHash that = (AuthTimeWithHash)o; return Objects.equals(hash, that.hash) + && Objects.equals(hashAlg, that.hashAlg) && Objects.equals(client, that.client) && Objects.equals(server, that.server) && ctime == that.ctime @@ -91,6 +119,19 @@ /** * Compares with a possibly old style object. Used * in DflCache$Storage#loadAndCheck. + * @return true if all AuthTime fields are the same but different hash + */ + public boolean sameTimeDiffHash(AuthTimeWithHash old) { + if (!this.isSameIgnoresHash(old)) { + return false; + } + return this.hashAlg.equals(old.hashAlg) && + !this.hash.equals(old.hash); + } + + /** + * Compares with a possibly old style object. Used + * in DflCache$Storage#loadAndCheck. * @return true if all AuthTime fields are the same */ public boolean isSameIgnoresHash(AuthTime old) { @@ -112,7 +153,7 @@ String sstring; if (withHash) { cstring = ""; - sstring = String.format("HASH:%s %d:%s %d:%s", hash, + sstring = String.format("%s:%s %d:%s %d:%s", hashAlg, hash, client.length(), client, server.length(), server); } else { --- old/src/java.security.jgss/share/classes/sun/security/krb5/internal/rcache/DflCache.java 2016-10-25 15:18:40.000000000 +0800 +++ new/src/java.security.jgss/share/classes/sun/security/krb5/internal/rcache/DflCache.java 2016-10-25 15:18:40.000000000 +0800 @@ -96,6 +96,8 @@ * Java also does this way. * * See src/lib/krb5/rcache/rc_io.c and src/lib/krb5/rcache/rc_dfl.c. + * + * Update: New version can use other hash algorithms. */ public class DflCache extends ReplayCache { @@ -300,7 +302,7 @@ if (time.equals(a)) { // Exact match, must be a replay throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT); - } else if (time.isSameIgnoresHash(a)) { + } else if (time.sameTimeDiffHash((AuthTimeWithHash)a)) { // Two different authenticators in the same second. // Remember it seeNewButNotSame = true; --- old/test/sun/security/krb5/auto/ReplayCacheExpunge.java 2016-10-25 15:18:42.000000000 +0800 +++ new/test/sun/security/krb5/auto/ReplayCacheExpunge.java 2016-10-25 15:18:42.000000000 +0800 @@ -47,15 +47,15 @@ int count = Integer.parseInt(args[0]); ReplayCache cache = ReplayCache.getInstance("dfl:./"); AuthTimeWithHash a1 = - new AuthTimeWithHash(client, server, time(-400), 0, hash("1")); + new AuthTimeWithHash(client, server, time(-400), 0, "HASH", hash("1")); AuthTimeWithHash a2 = - new AuthTimeWithHash(client, server, time(0), 0, hash("4")); + new AuthTimeWithHash(client, server, time(0), 0, "HASH", hash("4")); KerberosTime now = new KerberosTime(time(0)*1000L); KerberosTime then = new KerberosTime(time(-300)*1000L); // Once upon a time, we added a lot of events for (int i=0; i) + * Example: J,N,N/krb5-1.14/lib/libgssapi_krb5.so + * + * - test.runs on runs + * Format: (req# | client# service#) acceptor# expected... + * Example: c0h0J0v,c1h1N0v,r0J1x means 1st req is new c0 to h0 sent to J0, + * 2nd req is new c1 to h1 sent to N0, + * 3rd req is old (1st replayed) sent to J1. + * For all old reqs, client# and service# MUST be - + * + * - test.autoruns on how many autoruns + * Format: number + */ public class ReplayCacheTestProc { - private static Proc[] ps; - private static Proc pc; + private static Proc[] pa; // all acceptors + private static Proc pi; // the single initiator private static List reqs = new ArrayList<>(); private static String HOST = "localhost"; @@ -65,18 +86,14 @@ public static void main0(String[] args) throws Exception { System.setProperty("java.security.krb5.conf", OneKDC.KRB5_CONF); if (args.length == 0) { // The controller - int ns = 5; // number of servers int nu = 5; // number of users - int nx = 50; // number of experiments - int np = 5; // number of peers (services) - int mode = 0; // native(1), random(0), java(-1) - boolean random = true; // random experiments choreograph - - // Do not test interop with native GSS on some platforms - String os = System.getProperty("os.name", "???"); - if (!os.startsWith("SunOS") && !os.startsWith("Linux")) { - mode = -1; - } + int nh = 5; // number of hosts (services) + String[] libs; // available acceptor types: + // J: java + // N: default native lib + // N: native lib with the given name + Ex[] result; + int numPerType = 2; // number of servers per type uid = jdk.internal.misc.VM.geteuid(); @@ -85,93 +102,151 @@ kdc.addPrincipal(user(i), OneKDC.PASS); } kdc.addPrincipalRandKey("krbtgt/" + OneKDC.REALM); - for (int i=0; i + if (libs[type].length() == 1) { + label = libs[type]; + } else { + label = "" + (char)('A' + type); + } + pa[i] = acceptor(libs[type], label + i % numPerType); + } + + String userRuns = System.getProperty("test.runs"); + + if (userRuns != null) { + String[] runs = userRuns.split(","); + result = new Ex[runs.length]; + for (int i = 0; i < runs.length; i++) { + boolean expected = false; + int req = -1; + int client = -1; + int host = -1; + UserRun run = new UserRun(runs[i]); + while (true) { + char type = run.nextAction(); + if (type == ' ') { + break; + } + switch (type) { + case 'r': + req = result[run.nextValue()].req; + break; + case 'c': + client = run.nextValue(); + break; + case 'h': + req = req(client, run.nextValue()); + break; + case 'J': + case 'N': + for (int j = 0; j < libs.length; j++) { + if (libs[j].equals("" + type)) { + host = j * numPerType + run.nextValue(); + break; + } + } + break; + case 'v': + expected = true; + break; + case 'x': + expected = false; + break; + default: // ABC... + host = (type - 'A') * numPerType + run.nextValue(); + } + } + result[i] = new Ex(i, req, host, expected); + } } else { + result = new Ex[Integer.parseInt( + System.getProperty("test.autoruns", "100"))]; Random r = new Random(); - for (int i=0; i 1) { + String lib = type.substring(1); + String libDir = lib.substring(0, lib.lastIndexOf('/')); + p.prop("sun.security.jgss.lib", lib) + .env("DYLD_LIBRARY_PATH", libDir) + .env("LD_LIBRARY_PATH", libDir); + } + } + return p.start(); } + // generates hash of authenticator inside ap-req inside initsectoken private static String hash(String req) throws Exception { byte[] data = Base64.getDecoder().decode(req); data = Arrays.copyOfRange(data, 17, data.length); - byte[] hash = MessageDigest.getInstance("MD5").digest(new APReq(data).authenticator.getBytes()); + byte[] hash = MessageDigest.getInstance( + AuthTimeWithHash.realAlg(AuthTimeWithHash.DEFAULT_HASH_ALG)) + .digest(new APReq(data).authenticator.getBytes()); char[] h = new char[hash.length * 2]; char[] hexConst = "0123456789ABCDEF".toCharArray(); for (int i=0; i