--- old/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java 2016-10-25 09:03:35.000000000 +0800 +++ new/src/java.security.jgss/share/classes/sun/security/krb5/KrbApReq.java 2016-10-25 09:03:35.000000000 +0800 @@ -301,9 +301,10 @@ 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"); @@ -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 09:03:37.000000000 +0800 +++ new/src/java.security.jgss/share/classes/sun/security/krb5/internal/rcache/AuthTime.java 2016-10-25 09:03:37.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 09:03:38.000000000 +0800 +++ new/src/java.security.jgss/share/classes/sun/security/krb5/internal/rcache/AuthTimeWithHash.java 2016-10-25 09:03:38.000000000 +0800 @@ -25,6 +25,8 @@ package sun.security.krb5.internal.rcache; +import sun.security.action.GetPropertyAction; + import java.util.Objects; /** @@ -34,14 +36,32 @@ public class AuthTimeWithHash extends AuthTime implements Comparable { + // The hash algorithm can be "HASH" or "SHA256". + public static String DEFAULT_HASH_ALG = GetPropertyAction + .privilegedGetProperty("jdk.krb5.rcache.hashalg", "HASH"); + + public static String realAlg(String alg) { + if (alg.equals("HASH")) { + return "MD5"; + } else if (alg.equals("SHA")) { + return "SHA-1"; + } else if (alg.startsWith("SHA") && !alg.startsWith("SHA-")) { + return "SHA-" + alg.substring(3); + } else { + return alg; + } + } + + 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 +76,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 +112,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 +146,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 09:03:39.000000000 +0800 +++ new/src/java.security.jgss/share/classes/sun/security/krb5/internal/rcache/DflCache.java 2016-10-25 09:03:39.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 09:03:40.000000000 +0800 +++ new/test/sun/security/krb5/auto/ReplayCacheExpunge.java 2016-10-25 09:03:40.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