--- a/jdk/src/share/classes/sun/security/jgss/krb5/AcceptSecContextToken.java Thu Jun 20 19:14:30 2013 +0100
+++ b/jdk/src/share/classes/sun/security/jgss/krb5/AcceptSecContextToken.java Fri Jun 21 18:26:13 2013 +0800
@@ -27,9 +27,10 @@
import org.ietf.jgss.*;
import java.io.InputStream;
-import java.io.OutputStream;
import java.io.IOException;
-import java.io.ByteArrayInputStream;
+import java.security.AccessController;
+
+import sun.security.action.GetBooleanAction;
import sun.security.krb5.*;
class AcceptSecContextToken extends InitialToken {
@@ -42,23 +43,19 @@
*/
public AcceptSecContextToken(Krb5Context context,
KrbApReq apReq)
- throws KrbException, IOException {
+ throws KrbException, IOException, GSSException {
- /*
- * RFC 1964, section 1.2 states:
- * (1) context key: uses Kerberos session key (or subkey, if
- * present in authenticator emitted by context initiator) directly
- *
- * This does not mention context acceptor. Hence we will not
- * generate a subkey on the acceptor side. Note: Our initiator will
- * still allow another acceptor to generate a subkey, even though
- * our acceptor does not do so.
- */
- boolean useSubkey = false;
+ boolean useSubkey = AccessController.doPrivileged(
+ new GetBooleanAction("sun.security.krb5.acceptor.subkey"));
boolean useSequenceNumber = true;
- apRep = new KrbApRep(apReq, useSequenceNumber, useSubkey);
+ EncryptionKey subKey = null;
+ if (useSubkey) {
+ subKey = new EncryptionKey(apReq.getCreds().getSessionKey());
+ context.setKey(Krb5Context.ACCEPTOR_SUBKEY, subKey);
+ }
+ apRep = new KrbApRep(apReq, useSequenceNumber, subKey);
context.resetMySequenceNumber(apRep.getSeqNumber().intValue());
--- a/jdk/src/share/classes/sun/security/krb5/EncryptionKey.java Thu Jun 20 19:14:30 2013 +0100
+++ b/jdk/src/share/classes/sun/security/krb5/EncryptionKey.java Fri Jun 21 18:26:13 2013 +0800
@@ -297,9 +297,11 @@
/**
* Generates a sub-sessionkey from a given session key.
+ *
+ * Used in AcceptSecContextToken and KrbApReq by acceptor- and initiator-
+ * side respectively.
*/
- // Used in KrbApRep, KrbApReq
- EncryptionKey(EncryptionKey key) throws KrbCryptoException {
+ public EncryptionKey(EncryptionKey key) throws KrbCryptoException {
// generate random sub-session key
keyValue = Confounder.bytes(key.keyValue.length);
for (int i = 0; i < keyValue.length; i++) {
--- a/jdk/src/share/classes/sun/security/krb5/KrbApRep.java Thu Jun 20 19:14:30 2013 +0100
+++ b/jdk/src/share/classes/sun/security/krb5/KrbApRep.java Fri Jun 21 18:26:13 2013 +0800
@@ -53,12 +53,10 @@
*/
// Used in AcceptSecContextToken
public KrbApRep(KrbApReq incomingReq,
- boolean useSeqNumber,
- boolean useSubKey) throws KrbException, IOException {
+ boolean useSeqNumber,
+ EncryptionKey subKey)
+ throws KrbException, IOException {
- EncryptionKey subKey =
- (useSubKey?
- new EncryptionKey(incomingReq.getCreds().getSessionKey()):null);
SeqNumber seqNum = new LocalSeqNumber();
init(incomingReq, subKey, seqNum);
--- a/jdk/src/share/classes/sun/security/krb5/KrbApReq.java Thu Jun 20 19:14:30 2013 +0100
+++ b/jdk/src/share/classes/sun/security/krb5/KrbApReq.java Fri Jun 21 18:26:13 2013 +0800
@@ -33,12 +33,14 @@
import sun.security.krb5.internal.*;
import sun.security.krb5.internal.crypto.*;
-import sun.security.krb5.internal.rcache.*;
import sun.security.jgss.krb5.Krb5AcceptCredential;
import java.net.InetAddress;
import sun.security.util.*;
import java.io.IOException;
import java.util.Arrays;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import sun.security.krb5.internal.rcache.AuthTimeWithHash;
/**
* This class encapsulates a KRB-AP-REQ that a client sends to a
@@ -53,11 +55,23 @@
private Credentials creds;
private APReq apReqMessg;
- private static CacheTable table = new CacheTable();
+ // Used by acceptor side
+ private static ReplayCache rcache = ReplayCache.getInstance();
private static boolean DEBUG = Krb5.DEBUG;
+ private static final char[] hexConst = "0123456789ABCDEF".toCharArray();
+
+ private static final MessageDigest md;
+
+ static {
+ try {
+ md = MessageDigest.getInstance("MD5");
+ } catch (NoSuchAlgorithmException ex) {
+ throw new RuntimeException("Impossible");
+ }
+ }
/**
- * Contructs a AP-REQ message to send to the peer.
+ * Constructs an AP-REQ message to send to the peer.
* @param tgsCred the <code>Credentials</code> to be used to construct the
* AP Request protocol message.
* @param mutualRequired Whether mutual authentication is required
@@ -81,7 +95,7 @@
*/
/**
- * Contructs a AP-REQ message to send to the peer.
+ * Constructs an AP-REQ message to send to the peer.
* @param tgsCred the <code>Credentials</code> to be used to construct the
* AP Request protocol message.
* @param mutualRequired Whether mutual authentication is required
@@ -125,7 +139,7 @@
}
/**
- * Contructs a AP-REQ message from the bytes received from the
+ * Constructs an AP-REQ message from the bytes received from the
* peer.
* @param message The message received from the peer
* @param keys <code>EncrtyptionKey</code>s to decrypt the message;
@@ -146,7 +160,7 @@
}
/**
- * Contructs a AP-REQ message from the bytes received from the
+ * Constructs an AP-REQ message from the bytes received from the
* peer.
* @param value The <code>DerValue</code> that contains the
* DER enoded AP-REQ protocol message
@@ -297,15 +311,19 @@
if (!authenticator.ctime.inClockSkew())
throw new KrbApErrException(Krb5.KRB_AP_ERR_SKEW);
- // start to check if it is a replay attack.
- AuthTime time =
- new AuthTime(authenticator.ctime.getTime(), authenticator.cusec);
- String client = authenticator.cname.toString();
- if (table.get(time, authenticator.cname.toString()) != null) {
- throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
- } else {
- table.put(client, time, System.currentTimeMillis());
+ byte[] hash = md.digest(apReqMessg.authenticator.cipher);
+ char[] h = new char[hash.length * 2];
+ for (int i=0; i<hash.length; i++) {
+ h[2*i] = hexConst[(hash[i]&0xff)>>4];
+ h[2*i+1] = hexConst[hash[i]&0xf];
}
+ AuthTimeWithHash time = new AuthTimeWithHash(
+ authenticator.cname.toString(),
+ apReqMessg.ticket.sname.toString(),
+ authenticator.ctime.getSeconds(),
+ authenticator.cusec,
+ new String(h));
+ rcache.checkAndStore(KerberosTime.now(), time);
if (initiator != null) {
// sender host address
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/krb5/internal/ReplayCache.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 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.krb5.internal;
+
+import sun.security.action.GetPropertyAction;
+import sun.security.krb5.internal.rcache.AuthTimeWithHash;
+import sun.security.krb5.internal.rcache.MemoryCache;
+import sun.security.krb5.internal.rcache.DflCache;
+
+import java.security.AccessController;
+
+/**
+ * Models the replay cache of an acceptor as described in
+ * RFC 4120 3.2.3.
+ * @since 1.8
+ */
+public abstract class ReplayCache {
+ public static ReplayCache getInstance(String type) {
+ if (type == null) {
+ return new MemoryCache();
+ } else if (type.equals("dfl") || type.startsWith("dfl:")) {
+ return new DflCache(type);
+ } else if (type.equals("none")) {
+ return new ReplayCache() {
+ @Override
+ public void checkAndStore(KerberosTime currTime, AuthTimeWithHash time)
+ throws KrbApErrException {
+ // no check at all
+ }
+ };
+ } else {
+ throw new IllegalArgumentException("Unknown type: " + type);
+ }
+ }
+ public static ReplayCache getInstance() {
+ String type = AccessController.doPrivileged(
+ new GetPropertyAction("sun.security.krb5.rcache"));
+ return getInstance(type);
+ }
+
+ /**
+ * Accepts or rejects an AuthTime.
+ * @param currTime the current time
+ * @param time AuthTimeWithHash object calculated from authenticator
+ * @throws KrbApErrException if the authenticator is a replay
+ */
+ public abstract void checkAndStore(KerberosTime currTime, AuthTimeWithHash time)
+ throws KrbApErrException;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/krb5/internal/rcache/AuthList.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,144 @@
+/*
+ * Copyright (c) 2000, 2012, 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.
+ */
+
+/*
+ *
+ * (C) Copyright IBM Corp. 1999 All Rights Reserved.
+ * Copyright 1997 The Open Group Research Institute. All rights reserved.
+ */
+
+package sun.security.krb5.internal.rcache;
+
+import sun.security.krb5.internal.Krb5;
+
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import sun.security.krb5.internal.KerberosTime;
+import sun.security.krb5.internal.KrbApErrException;
+
+/**
+ * This class provides an efficient caching mechanism to store AuthTimeWithHash
+ * from client authenticators. The cache minimizes the memory usage by doing
+ * self-cleanup of expired items in the cache.
+ *
+ * AuthTimeWithHash objects inside a cache are always sorted from big (new) to
+ * small (old) as determined by {@see AuthTimeWithHash#compareTo}. In the most
+ * common case a newcomer should be newer than the first element.
+ *
+ * @author Yanni Zhang
+ */
+public class AuthList {
+
+ private final LinkedList<AuthTimeWithHash> entries;
+ private final int lifespan;
+
+ /**
+ * Constructs a AuthList.
+ */
+ public AuthList(int lifespan) {
+ this.lifespan = lifespan;
+ entries = new LinkedList<>();
+ }
+
+ /**
+ * Puts the authenticator timestamp into the cache in descending order,
+ * and throw an exception if it's already there.
+ */
+ public void put(AuthTimeWithHash t, KerberosTime currentTime)
+ throws KrbApErrException {
+
+ if (entries.isEmpty()) {
+ entries.addFirst(t);
+ } else {
+ AuthTimeWithHash temp = entries.getFirst();
+ int cmp = temp.compareTo(t);
+ if (cmp < 0) {
+ // This is the most common case, newly received authenticator
+ // has larger timestamp.
+ entries.addFirst(t);
+ } else if (cmp == 0) {
+ throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
+ } else {
+ //unless client clock being re-adjusted.
+ ListIterator<AuthTimeWithHash> it = entries.listIterator(1);
+ boolean found = false;
+ while (it.hasNext()) {
+ temp = it.next();
+ cmp = temp.compareTo(t);
+ if (cmp < 0) {
+ // Find an older one, put in front of it
+ entries.add(entries.indexOf(temp), t);
+ found = true;
+ break;
+ } else if (cmp == 0) {
+ throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
+ }
+ }
+ if (!found) {
+ // All is newer than the newcomer. Sigh.
+ entries.addLast(t);
+ }
+ }
+ }
+
+ // let us cleanup while we are here
+ long timeLimit = currentTime.getSeconds() - lifespan;
+ ListIterator<AuthTimeWithHash> it = entries.listIterator(0);
+ AuthTimeWithHash temp = null;
+ int index = -1;
+ while (it.hasNext()) {
+ // search expired timestamps.
+ temp = it.next();
+ if (temp.ctime < timeLimit) {
+ index = entries.indexOf(temp);
+ break;
+ }
+ }
+ // It would be nice if LinkedList has a method called truncate(index).
+ if (index > -1) {
+ do {
+ // remove expired timestamps from the list.
+ entries.removeLast();
+ } while(entries.size() > index);
+ }
+ }
+
+ public boolean isEmpty() {
+ return entries.isEmpty();
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ Iterator<AuthTimeWithHash> iter = entries.descendingIterator();
+ int pos = entries.size();
+ while (iter.hasNext()) {
+ AuthTimeWithHash at = iter.next();
+ sb.append('#').append(pos--).append(": ")
+ .append(at.toString()).append('\n');
+ }
+ return sb.toString();
+ }
+}
--- a/jdk/src/share/classes/sun/security/krb5/internal/rcache/AuthTime.java Thu Jun 20 19:14:30 2013 +0100
+++ b/jdk/src/share/classes/sun/security/krb5/internal/rcache/AuthTime.java Fri Jun 21 18:26:13 2013 +0800
@@ -1,4 +1,5 @@
/*
+ * Copyright (c) 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
@@ -30,54 +31,126 @@
package sun.security.krb5.internal.rcache;
-import sun.security.krb5.internal.KerberosTime;
+import java.io.IOException;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.charset.StandardCharsets;
+import java.util.StringTokenizer;
/**
- * The class represents the timestamp in authenticator.
+ * The class represents an old style replay cache entry. It is only used in
+ * a dfl file.
*
+ * @author Sun/Oracle
* @author Yanni Zhang
*/
public class AuthTime {
- long kerberosTime;
- int cusec;
+ final int ctime;
+ final int cusec;
+ final String client;
+ final String server;
+
+ /**
+ * Constructs an <code>AuthTime</code>.
+ */
+ public AuthTime(String client, String server,
+ int ctime, int cusec) {
+ this.ctime = ctime;
+ this.cusec = cusec;
+ this.client = client;
+ this.server = server;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%d/%06d/----/%s", ctime, cusec, client);
+ }
+
+ // Methods used when saved in a dfl file. See DflCache.java
/**
- * Constructs a new <code>AuthTime</code>.
- * @param time time from the <code>Authenticator</code>.
- * @param cusec microsecond field from the <code>Authenticator</code>.
+ * Reads an LC style string from a channel, which is a int32 length
+ * plus a UTF-8 encoded string possibly ends with \0.
+ * @throws IOException if there is a format error
+ * @throws BufferUnderflowException if goes beyond the end
*/
- public AuthTime(long time, int c) {
- kerberosTime = time;
- cusec = c;
+ private static String readStringWithLength(SeekableByteChannel chan)
+ throws IOException {
+ ByteBuffer bb = ByteBuffer.allocate(4);
+ bb.order(ByteOrder.nativeOrder());
+ chan.read(bb);
+ bb.flip();
+ int len = bb.getInt();
+ if (len > 1024) {
+ // Memory attack? The string should be fairly short.
+ throw new IOException("Invalid string length");
+ }
+ bb = ByteBuffer.allocate(len);
+ if (chan.read(bb) != len) {
+ throw new IOException("Not enough string");
+ }
+ byte[] data = bb.array();
+ return (data[len-1] == 0)?
+ new String(data, 0, len-1, StandardCharsets.UTF_8):
+ new String(data, StandardCharsets.UTF_8);
}
/**
- * Compares if an object equals to an <code>AuthTime</code> object.
- * @param o an object.
- * @return true if two objects are equivalent, otherwise, return false.
+ * Reads an AuthTime or AuthTimeWithHash object from a channel.
+ * @throws IOException if there is a format error
+ * @throws BufferUnderflowException if goes beyond the end
*/
- public boolean equals(Object o) {
- if (o instanceof AuthTime) {
- if ((((AuthTime)o).kerberosTime == kerberosTime)
- && (((AuthTime)o).cusec == cusec)) {
- return true;
+ public static AuthTime readFrom(SeekableByteChannel chan)
+ throws IOException {
+ String client = readStringWithLength(chan);
+ String server = readStringWithLength(chan);
+ ByteBuffer bb = ByteBuffer.allocate(8);
+ chan.read(bb);
+ bb.order(ByteOrder.nativeOrder());
+ int cusec = bb.getInt(0);
+ int ctime = bb.getInt(4);
+ if (client.isEmpty()) {
+ StringTokenizer st = new StringTokenizer(server, " :");
+ if (st.countTokens() != 6) {
+ throw new IOException("Incorrect rcache style");
}
+ st.nextToken();
+ String hash = st.nextToken();
+ st.nextToken();
+ client = st.nextToken();
+ st.nextToken();
+ server = st.nextToken();
+ return new AuthTimeWithHash(
+ client, server, ctime, cusec, hash);
+ } else {
+ return new AuthTime(
+ client, server, ctime, cusec);
}
- return false;
}
/**
- * Returns a hash code for this <code>AuthTime</code> object.
- *
- * @return a <code>hash code</code> value for this object.
+ * Encodes to be used in a dfl file
*/
- public int hashCode() {
- int result = 17;
-
- result = 37 * result + (int)(kerberosTime ^ (kerberosTime >>> 32));
- result = 37 * result + cusec;
-
- return result;
+ protected byte[] encode0(String cstring, String sstring) {
+ byte[] c = cstring.getBytes(StandardCharsets.UTF_8);;
+ byte[] s = sstring.getBytes(StandardCharsets.UTF_8);;
+ byte[] zero = new byte[1];
+ int len = 4 + c.length + 1 + 4 + s.length + 1 + 4 + 4;
+ ByteBuffer bb = ByteBuffer.allocate(len)
+ .order(ByteOrder.nativeOrder());
+ bb.putInt(c.length+1).put(c).put(zero)
+ .putInt(s.length+1).put(s).put(zero)
+ .putInt(cusec).putInt(ctime);
+ return bb.array();
}
+ /**
+ * Encodes to be used in a dfl file
+ * @param withHash useless here
+ */
+ public byte[] encode(boolean withHash) {
+ return encode0(client, server);
+ }
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/krb5/internal/rcache/AuthTimeWithHash.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 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.krb5.internal.rcache;
+
+import java.util.Objects;
+
+/**
+ * The class represents a new style replay cache entry. It can be either used
+ * inside memory or in a dfl file.
+ */
+public class AuthTimeWithHash extends AuthTime
+ implements Comparable<AuthTimeWithHash> {
+
+ final String hash;
+
+ /**
+ * Constructs a new <code>AuthTimeWithHash</code>.
+ */
+ public AuthTimeWithHash(String client, String server,
+ int ctime, int cusec, String hash) {
+ super(client, server, ctime, cusec);
+ this.hash = hash;
+ }
+
+ /**
+ * Compares if an object equals to an <code>AuthTimeWithHash</code> object.
+ * @param o an object.
+ * @return true if two objects are equivalent, otherwise, return false.
+ */
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof AuthTimeWithHash)) return false;
+ AuthTimeWithHash that = (AuthTimeWithHash)o;
+ return Objects.equals(hash, that.hash)
+ && Objects.equals(client, that.client)
+ && Objects.equals(server, that.server)
+ && ctime == that.ctime
+ && cusec == that.cusec;
+ }
+
+ /**
+ * Returns a hash code for this <code>AuthTimeWithHash</code> object.
+ */
+ @Override
+ public int hashCode() {
+ return Objects.hash(hash);
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%d/%06d/%s/%s", ctime, cusec, hash, client);
+ }
+
+ @Override
+ public int compareTo(AuthTimeWithHash other) {
+ int cmp = 0;
+ if (ctime != other.ctime) {
+ cmp = Integer.compare(ctime, other.ctime);
+ } else if (cusec != other.cusec) {
+ cmp = Integer.compare(cusec, other.cusec);
+ } else {
+ cmp = hash.compareTo(other.hash);
+ }
+ return cmp;
+ }
+
+ /**
+ * 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) {
+ return client.equals(old.client) &&
+ server.equals(old.server) &&
+ ctime == old.ctime &&
+ cusec == old.cusec;
+ }
+
+ // Methods used when saved in a dfl file. See DflCache.java
+
+ /**
+ * Encodes to be used in a dfl file
+ * @param withHash write new style if true
+ */
+ @Override
+ public byte[] encode(boolean withHash) {
+ String cstring;
+ String sstring;
+ if (withHash) {
+ cstring = "";
+ sstring = String.format("HASH:%s %d:%s %d:%s", hash,
+ client.length(), client,
+ server.length(), server);
+ } else {
+ cstring = client;
+ sstring = server;
+ }
+ return encode0(cstring, sstring);
+ }
+}
--- a/jdk/src/share/classes/sun/security/krb5/internal/rcache/CacheTable.java Thu Jun 20 19:14:30 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,91 +0,0 @@
-/*
- * 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.
- */
-
-/*
- *
- * (C) Copyright IBM Corp. 1999 All Rights Reserved.
- * Copyright 1997 The Open Group Research Institute. All rights reserved.
- */
-
-package sun.security.krb5.internal.rcache;
-
-import java.util.Hashtable;
-
-/**
- * This class implements Hashtable to store the replay caches.
- *
- * @author Yanni Zhang
- */
-public class CacheTable extends Hashtable<String,ReplayCache> {
-
- private static final long serialVersionUID = -4695501354546664910L;
-
- private boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
- public CacheTable () {
- }
-
- /**
- * Puts the client timestamp in replay cache.
- * @params principal the client's principal name.
- * @params time authenticator timestamp.
- */
- public synchronized void put(String principal, AuthTime time, long currTime) {
- ReplayCache rc = super.get(principal);
- if (rc == null) {
- if (DEBUG) {
- System.out.println("replay cache for " + principal + " is null.");
- }
- rc = new ReplayCache(principal, this);
- rc.put(time, currTime);
- if (!rc.isEmpty()) {
- super.put(principal, rc);
- }
- }
- else {
- rc.put(time, currTime);
- if (rc.isEmpty()) {
- super.remove(rc);
- }
- if (DEBUG) {
- System.out.println("replay cache found.");
- }
- }
-
- }
-
- /**
- * This method tests if replay cache keeps a record of the authenticator's time stamp.
- * If there is a record (replay attack detected), the server should reject the client request.
- * @params principal the client's principal name.
- * @params time authenticator timestamp.
- * @return null if no record found, else return an <code>AuthTime</code> object.
- */
- public Object get(AuthTime time, String principal) {
- ReplayCache rc = super.get(principal);
- if ((rc != null) && (rc.contains(time))) {
- return time;
- }
- return null;
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/krb5/internal/rcache/DflCache.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 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.krb5.internal.rcache;
+
+import java.io.*;
+import java.nio.BufferUnderflowException;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.nio.file.attribute.PosixFilePermission;
+import java.security.AccessController;
+import java.util.*;
+
+import sun.security.action.GetPropertyAction;
+import sun.security.krb5.internal.KerberosTime;
+import sun.security.krb5.internal.Krb5;
+import sun.security.krb5.internal.KrbApErrException;
+import sun.security.krb5.internal.ReplayCache;
+
+
+/**
+ * A dfl file is used to sustores AuthTime entries when the system property
+ * sun.security.krb5.rcache is set to
+ *
+ * dfl(|:path/|:path/name|:name)
+ *
+ * The file will be path/name. If path is not given, it will be
+ *
+ * System.getProperty("java.io.tmpdir")
+ *
+ * If name is not given, it will be
+ *
+ * service_euid
+ *
+ * Java does not have a method to get euid, so uid is used instead. This
+ * should normally to be since a Java program is seldom used as a setuid app.
+ *
+ * The file has a header:
+ *
+ * i16 0x0501 (KRB5_RC_VNO) in network order
+ * i32 number of seconds for lifespan (in native order, same below)
+ *
+ * followed by cache entries concatenated, which can be encoded in
+ * 2 styles:
+ *
+ * The traditional style is:
+ *
+ * LC of client principal
+ * LC of server principal
+ * i32 cusec of Authenticator
+ * i32 ctime of Authenticator
+ *
+ * The new style has a hash:
+ *
+ * LC of ""
+ * LC of "HASH:%s %lu:%s %lu:%s" of (hash, clientlen, client, serverlen,
+ * server) where msghash is 32 char (lower case) text mode md5sum
+ * of the ciphertext of authenticator.
+ * i32 cusec of Authenticator
+ * i32 ctime of Authenticator
+ *
+ * where LC of a string means
+ *
+ * i32 strlen(string) + 1
+ * octets of string, with the \0x00 ending
+ *
+ * The old style block is always created by MIT krb5 used even if a new style
+ * is available, which means there can be 2 entries for a single Authenticator.
+ * Java also does this way.
+ *
+ * See src/lib/krb5/rcache/rc_io.c and src/lib/krb5/rcache/rc_dfl.c.
+ */
+public class DflCache extends ReplayCache {
+
+ private static final int KRB5_RV_VNO = 0x501;
+ private static final int EXCESSREPS = 30; // if missed-hit>this, recreate
+
+ private final String source;
+
+ private static int uid;
+ static {
+ try {
+ // Available on Solaris, Linux and Mac. Otherwise, no _euid suffix
+ Class<?> clazz = Class.forName("com.sun.security.auth.module.UnixSystem");
+ uid = (int)(long)(Long)
+ clazz.getMethod("getUid").invoke(clazz.newInstance());
+ } catch (Exception e) {
+ uid = -1;
+ }
+ }
+
+ public DflCache (String source) {
+ this.source = source;
+ }
+
+ private static String defaultPath() {
+ return AccessController.doPrivileged(
+ new GetPropertyAction("java.io.tmpdir"));
+ }
+
+ private static String defaultFile(String server) {
+ // service/host@REALM -> service
+ int slash = server.indexOf('/');
+ if (slash == -1) {
+ // A normal principal? say, dummy@REALM
+ slash = server.indexOf('@');
+ }
+ if (slash != -1) {
+ // Should not happen, but be careful
+ server= server.substring(0, slash);
+ }
+ if (uid != -1) {
+ server += "_" + uid;
+ }
+ return server;
+ }
+
+ private static Path getFileName(String source, String server) {
+ String path, file;
+ if (source.equals("dfl")) {
+ path = defaultPath();
+ file = defaultFile(server);
+ } else if (source.startsWith("dfl:")) {
+ source = source.substring(4);
+ int pos = source.lastIndexOf('/');
+ int pos1 = source.lastIndexOf('\\');
+ if (pos1 > pos) pos = pos1;
+ if (pos == -1) {
+ // Only file name
+ path = defaultPath();
+ file = source;
+ } else if (new File(source).isDirectory()) {
+ // Only path
+ path = source;
+ file = defaultFile(server);
+ } else {
+ // Full pathname
+ path = null;
+ file = source;
+ }
+ } else {
+ throw new IllegalArgumentException();
+ }
+ return new File(path, file).toPath();
+ }
+
+ @Override
+ public void checkAndStore(KerberosTime currTime, AuthTimeWithHash time)
+ throws KrbApErrException {
+ try {
+ checkAndStore0(currTime, time);
+ } catch (IOException ioe) {
+ KrbApErrException ke = new KrbApErrException(Krb5.KRB_ERR_GENERIC);
+ ke.initCause(ioe);
+ throw ke;
+ }
+ }
+
+ private synchronized void checkAndStore0(KerberosTime currTime, AuthTimeWithHash time)
+ throws IOException, KrbApErrException {
+ Path p = getFileName(source, time.server);
+ int missed = 0;
+ try (Storage s = new Storage()) {
+ try {
+ missed = s.loadAndCheck(p, time, currTime);
+ } catch (IOException ioe) {
+ // Non-existing or invalid file
+ Storage.create(p);
+ missed = s.loadAndCheck(p, time, currTime);
+ }
+ s.append(time);
+ }
+ if (missed > EXCESSREPS) {
+ Storage.expunge(p, currTime);
+ }
+ }
+
+
+ private static class Storage implements Closeable {
+ // Static methods
+ @SuppressWarnings("try")
+ private static void create(Path p) throws IOException {
+ try (SeekableByteChannel newChan = createNoClose(p)) {
+ // Do nothing, wait for close
+ }
+ makeMine(p);
+ }
+
+ private static void makeMine(Path p) throws IOException {
+ // chmod to owner-rw only, otherwise MIT krb5 rejects
+ try {
+ Set<PosixFilePermission> attrs = new HashSet<>();
+ attrs.add(PosixFilePermission.OWNER_READ);
+ attrs.add(PosixFilePermission.OWNER_WRITE);
+ Files.setPosixFilePermissions(p, attrs);
+ } catch (UnsupportedOperationException uoe) {
+ // No POSIX permission. That's OK.
+ }
+ }
+
+ private static SeekableByteChannel createNoClose(Path p)
+ throws IOException {
+ SeekableByteChannel newChan = Files.newByteChannel(
+ p, StandardOpenOption.CREATE,
+ StandardOpenOption.TRUNCATE_EXISTING,
+ StandardOpenOption.WRITE);
+ ByteBuffer buffer = ByteBuffer.allocate(6);
+ buffer.putShort((short)KRB5_RV_VNO);
+ buffer.order(ByteOrder.nativeOrder());
+ buffer.putInt(KerberosTime.getDefaultSkew());
+ buffer.flip();
+ newChan.write(buffer);
+ return newChan;
+ }
+
+ private static void expunge(Path p, KerberosTime currTime)
+ throws IOException {
+ Path p2 = Files.createTempFile(p.getParent(), "rcache", null);
+ try (SeekableByteChannel oldChan = Files.newByteChannel(p);
+ SeekableByteChannel newChan = createNoClose(p2)) {
+ long timeLimit = currTime.getSeconds() - readHeader(oldChan);
+ while (true) {
+ try {
+ AuthTime at = AuthTime.readFrom(oldChan);
+ if (at.ctime > timeLimit) {
+ ByteBuffer bb = ByteBuffer.wrap(at.encode(true));
+ newChan.write(bb);
+ }
+ } catch (BufferUnderflowException e) {
+ break;
+ }
+ }
+ }
+ makeMine(p2);
+ Files.move(p2, p,
+ StandardCopyOption.REPLACE_EXISTING,
+ StandardCopyOption.ATOMIC_MOVE);
+ }
+
+ // Instance methods
+ SeekableByteChannel chan;
+ private int loadAndCheck(Path p, AuthTimeWithHash time,
+ KerberosTime currTime)
+ throws IOException, KrbApErrException {
+ int missed = 0;
+ if (Files.isSymbolicLink(p)) {
+ throw new IOException("Symlink not accepted");
+ }
+ try {
+ Set<PosixFilePermission> perms =
+ Files.getPosixFilePermissions(p);
+ if (uid != -1 &&
+ (Integer)Files.getAttribute(p, "unix:uid") != uid) {
+ throw new IOException("Not mine");
+ }
+ if (perms.contains(PosixFilePermission.GROUP_READ) ||
+ perms.contains(PosixFilePermission.GROUP_WRITE) ||
+ perms.contains(PosixFilePermission.GROUP_EXECUTE) ||
+ perms.contains(PosixFilePermission.OTHERS_READ) ||
+ perms.contains(PosixFilePermission.OTHERS_WRITE) ||
+ perms.contains(PosixFilePermission.OTHERS_EXECUTE)) {
+ throw new IOException("Accessible by someone else");
+ }
+ } catch (UnsupportedOperationException uoe) {
+ // No POSIX permissions? Ignore it.
+ }
+ chan = Files.newByteChannel(p, StandardOpenOption.WRITE,
+ StandardOpenOption.READ);
+
+ long timeLimit = currTime.getSeconds() - readHeader(chan);
+
+ long pos = 0;
+ boolean seeNewButNotSame = false;
+ while (true) {
+ try {
+ pos = chan.position();
+ AuthTime a = AuthTime.readFrom(chan);
+ if (a instanceof AuthTimeWithHash) {
+ if (time.equals(a)) {
+ // Exact match, must be a replay
+ throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
+ } else if (time.isSameIgnoresHash(a)) {
+ // Two different authenticators in the same second.
+ // Remember it
+ seeNewButNotSame = true;
+ }
+ } else {
+ if (time.isSameIgnoresHash(a)) {
+ // Two authenticators in the same second. Considered
+ // same if we haven't seen a new style version of it
+ if (!seeNewButNotSame) {
+ throw new KrbApErrException(Krb5.KRB_AP_ERR_REPEAT);
+ }
+ }
+ }
+ if (a.ctime < timeLimit) {
+ missed++;
+ } else {
+ missed--;
+ }
+ } catch (BufferUnderflowException e) {
+ // Half-written file?
+ chan.position(pos);
+ break;
+ }
+ }
+ return missed;
+ }
+
+ private static int readHeader(SeekableByteChannel chan)
+ throws IOException {
+ ByteBuffer bb = ByteBuffer.allocate(6);
+ chan.read(bb);
+ if (bb.getShort(0) != KRB5_RV_VNO) {
+ throw new IOException("Not correct rcache version");
+ }
+ bb.order(ByteOrder.nativeOrder());
+ return bb.getInt(2);
+ }
+
+ private void append(AuthTimeWithHash at) throws IOException {
+ // Write an entry with hash, to be followed by one without it,
+ // for the benefit of old implementations.
+ ByteBuffer bb;
+ bb = ByteBuffer.wrap(at.encode(true));
+ chan.write(bb);
+ bb = ByteBuffer.wrap(at.encode(false));
+ chan.write(bb);
+ }
+
+ @Override
+ public void close() throws IOException {
+ if (chan != null) chan.close();
+ chan = null;
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/classes/sun/security/krb5/internal/rcache/MemoryCache.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 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.
+ */
+
+/*
+ *
+ * (C) Copyright IBM Corp. 1999 All Rights Reserved.
+ * Copyright 1997 The Open Group Research Institute. All rights reserved.
+ */
+
+package sun.security.krb5.internal.rcache;
+
+import java.util.*;
+import sun.security.krb5.internal.KerberosTime;
+import sun.security.krb5.internal.KrbApErrException;
+import sun.security.krb5.internal.ReplayCache;
+
+/**
+ * This class stores replay caches. AuthTimeWithHash objects are categorized
+ * into AuthLists keyed by the names of client and server.
+ *
+ * @author Yanni Zhang
+ */
+public class MemoryCache extends ReplayCache {
+
+ // TODO: One day we'll need to read dynamic krb5.conf.
+ private static final int lifespan = KerberosTime.getDefaultSkew();
+ private static final boolean DEBUG = sun.security.krb5.internal.Krb5.DEBUG;
+
+ private final Map<String,AuthList> content = new HashMap<>();
+
+ @Override
+ public synchronized void checkAndStore(KerberosTime currTime, AuthTimeWithHash time)
+ throws KrbApErrException {
+ String key = time.client + "|" + time.server;
+ AuthList rc = content.get(key);
+ if (DEBUG) {
+ System.out.println("MemoryCache: add " + time + " to " + key);
+ }
+ if (rc == null) {
+ rc = new AuthList(lifespan);
+ rc.put(time, currTime);
+ if (!rc.isEmpty()) {
+ content.put(key, rc);
+ }
+ } else {
+ if (DEBUG) {
+ System.out.println("MemoryCache: Existing AuthList:\n" + rc);
+ }
+ rc.put(time, currTime);
+ if (rc.isEmpty()) {
+ content.remove(key);
+ }
+ }
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (AuthList rc: content.values()) {
+ sb.append(rc.toString());
+ }
+ return sb.toString();
+ }
+}
--- a/jdk/src/share/classes/sun/security/krb5/internal/rcache/ReplayCache.java Thu Jun 20 19:14:30 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-/*
- * Copyright (c) 2000, 2012, 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.
- */
-
-/*
- *
- * (C) Copyright IBM Corp. 1999 All Rights Reserved.
- * Copyright 1997 The Open Group Research Institute. All rights reserved.
- */
-
-package sun.security.krb5.internal.rcache;
-
-import sun.security.krb5.internal.Krb5;
-import java.util.LinkedList;
-import java.util.ListIterator;
-import sun.security.krb5.internal.KerberosTime;
-
-/**
- * This class provides an efficient caching mechanism to store the timestamp of client authenticators.
- * The cache minimizes the memory usage by doing self-cleanup of expired items in the cache.
- *
- * @author Yanni Zhang
- */
-public class ReplayCache extends LinkedList<AuthTime> {
-
- private static final long serialVersionUID = 2997933194993803994L;
-
- // These 3 fields are now useless, keep for serialization compatibility
- private String principal;
- private CacheTable table;
- private int nap = 10 * 60 * 1000; //10 minutes break
-
- private boolean DEBUG = Krb5.DEBUG;
-
- /**
- * Constructs a ReplayCache for a client principal in specified <code>CacheTable</code>.
- * @param p client principal name.
- * @param ct CacheTable.
- */
- public ReplayCache (String p, CacheTable ct) {
- principal = p;
- table = ct;
- }
-
- /**
- * Puts the authenticator timestamp into the cache in descending order.
- * @param t <code>AuthTime</code>
- */
- public synchronized void put(AuthTime t, long currentTime) {
-
- if (this.size() == 0) {
- addFirst(t);
- }
- else {
- AuthTime temp = getFirst();
- if (temp.kerberosTime < t.kerberosTime) {
- // in most cases, newly received authenticator has
- // larger timestamp.
- addFirst(t);
- }
- else if (temp.kerberosTime == t.kerberosTime) {
- if (temp.cusec < t.cusec) {
- addFirst(t);
- }
- }
- else {
- //unless client clock being re-adjusted.
- ListIterator<AuthTime> it = listIterator(1);
- while (it.hasNext()) {
- temp = it.next();
- if (temp.kerberosTime < t.kerberosTime) {
- add(indexOf(temp), t);
- break;
- //we always put the bigger timestamp at the front.
- }
- else if (temp.kerberosTime == t.kerberosTime) {
- if (temp.cusec < t.cusec) {
- add(indexOf(temp), t);
- break;
- }
- }
- }
- }
- }
-
- // let us cleanup while we are here
- long timeLimit = currentTime - KerberosTime.getDefaultSkew() * 1000L;
- ListIterator<AuthTime> it = listIterator(0);
- AuthTime temp = null;
- int index = -1;
- while (it.hasNext()) {
- //search expired timestamps.
- temp = it.next();
- if (temp.kerberosTime < timeLimit) {
- index = indexOf(temp);
- break;
- }
- }
- if (index > -1) {
- do {
- //remove expired timestamps from the list.
- removeLast();
- } while(size() > index);
- }
- if (DEBUG) {
- printList();
- }
- }
-
-
- /**
- * Prints out the debug message.
- */
- private void printList() {
- Object[] total = toArray();
- for (int i = 0; i < total.length; i++) {
- System.out.println("object " + i + ": " + ((AuthTime)total[i]).kerberosTime + "/"
- + ((AuthTime)total[i]).cusec);
- }
- }
-
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/java/security/testlibrary/Proc.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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.
+ */
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.security.Permission;
+import java.util.ArrayList;
+import java.util.Base64;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * This is a test library that makes writing a Java test that spawns multiple
+ * Java processes easily.
+ *
+ * Usage:
+ *
+ * Proc.create("Clazz") // The class to launch
+ * .args("x") // with args
+ * .env("env", "value") // and an environment variable
+ * .prop("key","value") // and a system property
+ * .perm(perm) // with granted permissions
+ * .start(); // and start
+ *
+ * create/start must be called, args/env/prop/perm can be called zero or
+ * multiple times between create and start.
+ *
+ * The controller can call inheritIO to share its I/O to the process.
+ * Otherwise, it can send data into a proc's stdin with write/println, and
+ * read its stdout with readLine. stderr is always redirected to DFILE
+ * unless nodump() is called. A protocol is designed to make
+ * data exchange among the controller and the processes super easy, in which
+ * useful data are always printed with a special prefix ("PROCISFUN:").
+ * If the data is binary, make it BASE64.
+ *
+ * For example:
+ *
+ * - A producer Proc calls Proc.binOut() or Proc.textOut() to send out data.
+ * This method would prints to the stdout something like
+ *
+ * PROCISFUN:[raw text or base64 binary]
+ *
+ * - The controller calls producer.readData() to get the content. This method
+ * ignores all other output and only reads lines starting with "PROCISFUN:".
+ *
+ * - The controller does not care if the context is text or base64, it simply
+ * feeds the data to a consumer Proc by calling consumer.println(data).
+ * This will be printed into System.in of the consumer process.
+ *
+ * - The consumer Proc calls Proc.binIn() or Proc.textIn() to read the data.
+ * The first method de-base64 the input and return a byte[] block.
+ *
+ * Please note only plain ASCII is supported in raw text at the moment.
+ *
+ * As the Proc objects are hidden so deeply, two static methods, d(String) and
+ * d(Throwable) are provided to output info into stderr, where they will
+ * normally be appended messages to DFILE (unless nodump() is called).
+ * Developers can view the messages in real time by calling
+ *
+ * tail -f proc.debug
+ *
+ * TODO:
+ *
+ * . launch java tools, say, keytool
+ * . launch another version of java
+ * . start in another directory
+ * . start and finish using one method
+ *
+ * This is not a test, but is the core of
+ * JDK-8009977: A test library to launch multiple Java processes
+ */
+public class Proc {
+ private Process p;
+ private BufferedReader br; // the stdout of a process
+ private String launcher; // Optional: the java program
+
+ private List<Permission> perms = new ArrayList<>();
+ private List<String> args = new ArrayList<>();
+ private Map<String,String> env = new HashMap<>();
+ private Map<String,String> prop = new HashMap();
+ private boolean inheritIO = false;
+ private boolean noDump = false;
+
+ private String clazz; // Class to launch
+ private String debug; // debug flag, controller will show data
+ // transfer between procs
+
+ final private static String PREFIX = "PROCISFUN:";
+ final private static String DFILE = "proc.debug";
+
+ // The following methods are called by controllers
+
+ // Creates a Proc by the Java class name, launcher is an optional
+ // argument to specify the java program
+ public static Proc create(String clazz, String... launcher) {
+ Proc pc = new Proc();
+ pc.clazz = clazz;
+ if (launcher.length > 0) {
+ pc.launcher = launcher[0];
+ }
+ return pc;
+ }
+ // Sets inheritIO flag to proc. If set, proc will same I/O channels as
+ // teh controller. Otherwise, its stdin/stdout is untouched, and its
+ // stderr is redirected to DFILE.
+ public Proc inheritIO() {
+ inheritIO = true;
+ return this;
+ }
+ // When called, stderr inherits parent stderr, otherwise, append to a file
+ public Proc nodump() {
+ noDump = true;
+ return this;
+ }
+ // Specifies some args. Can be called multiple times.
+ public Proc args(String... args) {
+ for (String c: args) {
+ this.args.add(c);
+ }
+ return this;
+ }
+ // Returns debug prefix
+ public String debug() {
+ return debug;
+ }
+ // Enables debug with prefix
+ public Proc debug(String title) {
+ debug = title;
+ return this;
+ }
+ // Specifies an env var. Can be called multiple times.
+ public Proc env(String a, String b) {
+ env.put(a, b);
+ return this;
+ }
+ // Specifies a Java system property. Can be called multiple times.
+ public Proc prop(String a, String b) {
+ prop.put(a, b);
+ return this;
+ }
+ // Adds a perm to policy. Can be called multiple times. In order to make it
+ // effective, please also call prop("java.security.manager", "").
+ public Proc perm(Permission p) {
+ perms.add(p);
+ return this;
+ }
+ // Starts the proc
+ public Proc start() throws IOException {
+ List<String> cmd = new ArrayList<>();
+ if (launcher != null) {
+ cmd.add(launcher);
+ } else {
+ cmd.add(new File(new File(System.getProperty("java.home"), "bin"),
+ "java").getPath());
+ }
+ cmd.add("-cp");
+ StringBuilder cp = new StringBuilder();
+ for (URL url: ((URLClassLoader)Proc.class.getClassLoader()).getURLs()) {
+ if (cp.length() != 0) {
+ cp.append(File.pathSeparatorChar);
+ }
+ cp.append(url.getFile());
+ }
+ cmd.add(cp.toString());
+ for (Entry<String,String> e: prop.entrySet()) {
+ cmd.add("-D" + e.getKey() + "=" + e.getValue());
+ }
+ if (!perms.isEmpty()) {
+ Path p = Files.createTempFile(
+ Paths.get(".").toAbsolutePath(), "policy", null);
+ StringBuilder sb = new StringBuilder();
+ sb.append("grant {\n");
+ for (Permission perm: perms) {
+ // Sometimes a permission has no name or actions.
+ // but it's safe to use an empty string.
+ String s = String.format("%s \"%s\", \"%s\"",
+ perm.getClass().getCanonicalName(),
+ perm.getName()
+ .replace("\\", "\\\\").replace("\"", "\\\""),
+ perm.getActions());
+ sb.append(" permission ").append(s).append(";\n");
+ }
+ sb.append("};\n");
+ Files.write(p, sb.toString().getBytes());
+ cmd.add("-Djava.security.policy=" + p.toString());
+ }
+ cmd.add(clazz);
+ for (String s: args) {
+ cmd.add(s);
+ }
+ if (debug != null) {
+ System.out.println("PROC: " + debug + " cmdline: " + cmd);
+ }
+ ProcessBuilder pb = new ProcessBuilder(cmd);
+ for (Entry<String,String> e: env.entrySet()) {
+ pb.environment().put(e.getKey(), e.getValue());
+ }
+ if (inheritIO) {
+ pb.inheritIO();
+ } else if (noDump) {
+ pb.redirectError(ProcessBuilder.Redirect.INHERIT);
+ } else {
+ pb.redirectError(ProcessBuilder.Redirect.appendTo(new File(DFILE)));
+ }
+ p = pb.start();
+ br = new BufferedReader(new InputStreamReader(p.getInputStream()));
+ return this;
+ }
+ // Reads a line from stdout of proc
+ public String readLine() throws IOException {
+ String s = br.readLine();
+ if (debug != null) {
+ System.out.println("PROC: " + debug + " readline: " +
+ (s == null ? "<EOF>" : s));
+ }
+ return s;
+ }
+ // Reads a special line from stdout of proc
+ public String readData() throws Exception {
+ while (true) {
+ String s = readLine();
+ if (s == null) {
+ if (p.waitFor() != 0) {
+ throw new Exception("Proc abnormal end");
+ } else {
+ return s;
+ }
+ }
+ if (s.startsWith(PREFIX)) {
+ return s.substring(PREFIX.length());
+ }
+ }
+ }
+ // Writes text into stdin of proc
+ public void println(String s) throws IOException {
+ if (debug != null) {
+ System.out.println("PROC: " + debug + " println: " + s);
+ }
+ write((s + "\n").getBytes());
+ }
+ // Writes data into stdin of proc
+ public void write(byte[] b) throws IOException {
+ p.getOutputStream().write(b);
+ p.getOutputStream().flush();
+ }
+ // Reads all output and wait for process end
+ public int waitFor() throws Exception {
+ while (true) {
+ String s = readLine();
+ if (s == null) {
+ break;
+ }
+ }
+ return p.waitFor();
+ }
+
+ // The following methods are used inside a proc
+
+ // Writes out a BASE64 binary with a prefix
+ public static void binOut(byte[] data) {
+ System.out.println(PREFIX + Base64.getEncoder().encodeToString(data));
+ }
+ // Reads in a line of BASE64 binary
+ public static byte[] binIn() throws Exception {
+ return Base64.getDecoder().decode(textIn());
+ }
+ // Writes out a text with a prefix
+ public static void textOut(String data) {
+ System.out.println(PREFIX + data);
+ }
+ // Reads in a line of text
+ public static String textIn() throws Exception {
+ StringBuilder sb = new StringBuilder();
+ boolean isEmpty = true;
+ while (true) {
+ int i = System.in.read();
+ if (i == -1) break;
+ isEmpty = false;
+ if (i == '\n') break;
+ if (i != 13) {
+ // Force it to a char, so only simple ASCII works.
+ sb.append((char)i);
+ }
+ }
+ return isEmpty ? null : sb.toString();
+ }
+ // Sends string to stderr. If inheritIO is not called, they will
+ // be collected into DFILE
+ public static void d(String s) throws IOException {
+ System.err.println(s);
+ }
+ // Sends an exception to stderr
+ public static void d(Throwable e) throws IOException {
+ e.printStackTrace();
+ }
+}
--- a/jdk/test/sun/security/krb5/auto/AcceptorSubKey.java Thu Jun 20 19:14:30 2013 +0100
+++ b/jdk/test/sun/security/krb5/auto/AcceptorSubKey.java Fri Jun 21 18:26:13 2013 +0800
@@ -26,10 +26,10 @@
* @bug 7077646
* @summary gssapi wrap for CFX per-message tokens always set FLAG_ACCEPTOR_SUBKEY
* @compile -XDignore.symbol.file AcceptorSubKey.java
- * @run main/othervm AcceptorSubKey
+ * @run main/othervm AcceptorSubKey 0
+ * @run main/othervm AcceptorSubKey 4
*/
-import java.util.Arrays;
import sun.security.jgss.GSSUtil;
// The basic krb5 test skeleton you can copy from
@@ -37,8 +37,14 @@
public static void main(String[] args) throws Exception {
+ int expected = Integer.parseInt(args[0]);
+
new OneKDC(null).writeJAASConf();
+ if (expected != 0) {
+ System.setProperty("sun.security.krb5.acceptor.subkey", "true");
+ }
+
Context c, s;
c = Context.fromJAAS("client");
s = Context.fromJAAS("server");
@@ -53,8 +59,8 @@
// FLAG_ACCEPTOR_SUBKEY is 4
int flagOn = wrapped[2] & 4;
- if (flagOn != 0) {
- throw new Exception("Java GSS should not have set acceptor subkey");
+ if (flagOn != expected) {
+ throw new Exception("not expected");
}
s.dispose();
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/BasicProc.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 8009977
+ * @summary A test library to launch multiple Java processes
+ * @library ../../../../java/security/testlibrary/
+ * @compile -XDignore.symbol.file BasicProc.java
+ * @run main/othervm BasicProc
+ */
+
+import java.io.File;
+import org.ietf.jgss.Oid;
+
+import javax.security.auth.PrivateCredentialPermission;
+
+public class BasicProc {
+
+ static String CONF = "krb5.conf";
+ static String KTAB = "ktab";
+ public static void main(String[] args) throws Exception {
+ String HOST = "localhost";
+ String SERVER = "server/" + HOST;
+ String BACKEND = "backend/" + HOST;
+ String USER = "user";
+ char[] PASS = "password".toCharArray();
+ String REALM = "REALM";
+
+ Oid oid = new Oid("1.2.840.113554.1.2.2");
+
+ if (args.length == 0) {
+ System.setProperty("java.security.krb5.conf", CONF);
+ KDC kdc = KDC.create(REALM, HOST, 0, true);
+ kdc.addPrincipal(USER, PASS);
+ kdc.addPrincipalRandKey("krbtgt/" + REALM);
+ kdc.addPrincipalRandKey(SERVER);
+ kdc.addPrincipalRandKey(BACKEND);
+
+ String cwd = System.getProperty("user.dir");
+ kdc.writeKtab(KTAB);
+ KDC.saveConfig(CONF, kdc, "forwardable = true");
+
+ Proc pc = Proc.create("BasicProc")
+ .args("client")
+ .prop("java.security.krb5.conf", CONF)
+ .prop("java.security.manager", "")
+ .perm(new java.util.PropertyPermission(
+ "sun.security.krb5.principal", "read"))
+ .perm(new javax.security.auth.AuthPermission(
+ "modifyPrincipals"))
+ .perm(new javax.security.auth.AuthPermission(
+ "modifyPrivateCredentials"))
+ .perm(new javax.security.auth.AuthPermission("doAs"))
+ .perm(new javax.security.auth.kerberos.ServicePermission(
+ "krbtgt/" + REALM + "@" + REALM, "initiate"))
+ .perm(new javax.security.auth.kerberos.ServicePermission(
+ "server/localhost@" + REALM, "initiate"))
+ .perm(new javax.security.auth.kerberos.DelegationPermission(
+ "\"server/localhost@" + REALM + "\" " +
+ "\"krbtgt/" + REALM + "@" + REALM + "\""))
+ .debug("C")
+ .start();
+ Proc ps = Proc.create("BasicProc")
+ .args("server")
+ .prop("java.security.krb5.conf", CONF)
+ .prop("java.security.manager", "")
+ .perm(new java.util.PropertyPermission(
+ "sun.security.krb5.principal", "read"))
+ .perm(new javax.security.auth.AuthPermission(
+ "modifyPrincipals"))
+ .perm(new javax.security.auth.AuthPermission(
+ "modifyPrivateCredentials"))
+ .perm(new javax.security.auth.AuthPermission("doAs"))
+ .perm(new PrivateCredentialPermission(
+ "javax.security.auth.kerberos.KeyTab * \"*\"",
+ "read"))
+ .perm(new javax.security.auth.kerberos.ServicePermission(
+ "server/localhost@" + REALM, "accept"))
+ .perm(new java.io.FilePermission(
+ cwd + File.separator + KTAB, "read"))
+ .perm(new javax.security.auth.kerberos.ServicePermission(
+ "backend/localhost@" + REALM, "initiate"))
+ .debug("S")
+ .start();
+ Proc pb = Proc.create("BasicProc")
+ .args("backend")
+ .prop("java.security.krb5.conf", CONF)
+ .prop("java.security.manager", "")
+ .perm(new java.util.PropertyPermission(
+ "sun.security.krb5.principal", "read"))
+ .perm(new javax.security.auth.AuthPermission(
+ "modifyPrincipals"))
+ .perm(new javax.security.auth.AuthPermission(
+ "modifyPrivateCredentials"))
+ .perm(new javax.security.auth.AuthPermission("doAs"))
+ .perm(new PrivateCredentialPermission(
+ "javax.security.auth.kerberos.KeyTab * \"*\"",
+ "read"))
+ .perm(new javax.security.auth.kerberos.ServicePermission(
+ "backend/localhost@" + REALM, "accept"))
+ .perm(new java.io.FilePermission(
+ cwd + File.separator + KTAB, "read"))
+ .debug("B")
+ .start();
+
+ // Client and server handshake
+ String token = pc.readData();
+ ps.println(token);
+ token = ps.readData();
+ pc.println(token);
+ // Server and backend handshake
+ token = ps.readData();
+ pb.println(token);
+ token = pb.readData();
+ ps.println(token);
+ // wrap/unwrap/getMic/verifyMic and plain text
+ token = ps.readData();
+ pb.println(token);
+ token = pb.readData();
+ ps.println(token);
+ token = pb.readData();
+ ps.println(token);
+
+ if ((pc.waitFor() | ps.waitFor() | pb.waitFor()) != 0) {
+ throw new Exception();
+ }
+ } else if (args[0].equals("client")) {
+ Context c = Context.fromUserPass(USER, PASS, false);
+ c.startAsClient(SERVER, oid);
+ c.x().requestCredDeleg(true);
+ Proc.binOut(c.take(new byte[0]));
+ byte[] token = Proc.binIn();
+ c.take(token);
+ } else if (args[0].equals("server")) {
+ Context s = Context.fromUserKtab(SERVER, KTAB, true);
+ s.startAsServer(oid);
+ byte[] token = Proc.binIn();
+ token = s.take(token);
+ Proc.binOut(token);
+ Context s2 = s.delegated();
+ s2.startAsClient(BACKEND, oid);
+ Proc.binOut(s2.take(new byte[0]));
+ token = Proc.binIn();
+ s2.take(token);
+ byte[] msg = "Hello".getBytes();
+ Proc.binOut(s2.wrap(msg, true));
+ s2.verifyMic(Proc.binIn(), msg);
+ String in = Proc.textIn();
+ if (!in.equals("Hello")) {
+ throw new Exception();
+ }
+ } else if (args[0].equals("backend")) {
+ Context b = Context.fromUserKtab(BACKEND, KTAB, true);
+ b.startAsServer(oid);
+ byte[] token = Proc.binIn();
+ Proc.binOut(b.take(token));
+ byte[] msg = b.unwrap(Proc.binIn(), true);
+ Proc.binOut(b.getMic(msg));
+ Proc.textOut(new String(msg));
+ }
+ }
+ // create a native server
+ private static Proc ns(Proc p) throws Exception {
+ return p
+ .env("KRB5_CONFIG", CONF)
+ .env("KRB5_KTNAME", KTAB)
+ .prop("sun.security.jgss.native", "true")
+ .prop("javax.security.auth.useSubjectCredsOnly", "false")
+ .prop("sun.security.nativegss.debug", "true");
+ }
+}
--- a/jdk/test/sun/security/krb5/auto/Context.java Thu Jun 20 19:14:30 2013 +0100
+++ b/jdk/test/sun/security/krb5/auto/Context.java Fri Jun 21 18:26:13 2013 +0800
@@ -195,6 +195,7 @@
Krb5LoginModule krb5 = new Krb5LoginModule();
Map<String, String> map = new HashMap<>();
+ map.put("isInitiator", "false");
map.put("doNotPrompt", "true");
map.put("useTicketCache", "false");
map.put("useKeyTab", "true");
@@ -616,9 +617,10 @@
*/
static public void handshake(final Context c, final Context s) throws Exception {
byte[] t = new byte[0];
- while (!c.x.isEstablished() || !s.x.isEstablished()) {
- t = c.take(t);
- t = s.take(t);
+ while (true) {
+ if (t != null || !c.x.isEstablished()) t = c.take(t);
+ if (t != null || !s.x.isEstablished()) t = s.take(t);
+ if (c.x.isEstablished() && s.x.isEstablished()) break;
}
}
}
--- a/jdk/test/sun/security/krb5/auto/KDC.java Thu Jun 20 19:14:30 2013 +0100
+++ b/jdk/test/sun/security/krb5/auto/KDC.java Fri Jun 21 18:26:13 2013 +0800
@@ -1137,7 +1137,7 @@
* @return REALM.NAME = { kdc = host:port }
*/
private static String realmLineForKDC(KDC kdc) {
- return String.format(" %s = {\n kdc = %s:%d\n }\n",
+ return String.format("%s = {\n kdc = %s:%d\n}\n",
kdc.realm,
kdc.kdc,
kdc.port);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/NoneReplayCacheTest.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2013 Sun Microsystems, Inc. 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+/*
+ * @test
+ * @bug 8001326
+ * @run main/othervm NoneReplayCacheTest
+ * @summary the replaycache type none cannot stop an authenticator replay,
+ * but it can stop a message replay when s.s.k.acceptor.subkey is true.
+ * You should not really use none in production environment. This test merely
+ * shows there can be other protections when replay cache is not working fine.
+ */
+
+import org.ietf.jgss.GSSException;
+import sun.security.jgss.GSSUtil;
+
+public class NoneReplayCacheTest {
+
+ public static void main(String[] args)
+ throws Exception {
+
+ new OneKDC(null);
+
+ System.setProperty("sun.security.krb5.rcache", "none");
+ System.setProperty("sun.security.krb5.acceptor.subkey", "true");
+
+ Context c, s;
+ c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
+ s = Context.fromUserKtab(OneKDC.SERVER, OneKDC.KTAB, true);
+
+ c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+ s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+
+ byte[] first = c.take(new byte[0]);
+
+ c.take(s.take(first));
+
+ byte[] msg = c.wrap("hello".getBytes(), true);
+ s.unwrap(msg, true);
+
+ s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+ s.take(first); // apreq replay not detectable
+ try {
+ s.unwrap(msg, true); // msg replay detectable
+ throw new Exception("This method should fail");
+ } catch (GSSException gsse) {
+ gsse.printStackTrace();
+ }
+ }
+}
--- a/jdk/test/sun/security/krb5/auto/ReplayCache.java Thu Jun 20 19:14:30 2013 +0100
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-/*
- * Copyright (c) 2012, 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 7118809
- * @run main/othervm ReplayCache
- * @summary rcache deadlock
- */
-
-import org.ietf.jgss.GSSException;
-import sun.security.jgss.GSSUtil;
-import sun.security.krb5.KrbException;
-import sun.security.krb5.internal.Krb5;
-
-public class ReplayCache {
-
- public static void main(String[] args)
- throws Exception {
-
- new OneKDC(null).writeJAASConf();
-
- Context c, s;
- c = Context.fromJAAS("client");
- s = Context.fromJAAS("server");
-
- c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
- s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
-
- byte[] first = c.take(new byte[0]);
- s.take(first);
-
- s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
- try {
- s.take(first); // Replay the last token sent
- throw new Exception("This method should fail");
- } catch (GSSException gsse) {
- KrbException ke = (KrbException)gsse.getCause();
- if (ke.returnCode() != Krb5.KRB_AP_ERR_REPEAT) {
- throw gsse;
- }
- }
- }
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/ReplayCacheExpunge.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 8001326
+ * @run main/othervm ReplayCacheExpunge 16
+ * @run main/othervm/fail ReplayCacheExpunge 15
+ * @summary when number of expired entries minus number of good entries
+ * is more than 30, expunge occurs, and expired entries are forgotten.
+*/
+
+import java.util.Random;
+import sun.security.krb5.internal.KerberosTime;
+import sun.security.krb5.internal.ReplayCache;
+import sun.security.krb5.internal.rcache.AuthTimeWithHash;
+
+public class ReplayCacheExpunge {
+ static final String client = "dummy@REALM";
+ static final String server = "server/localhost@REALM";
+ static final Random rand = new Random();
+
+ public static void main(String[] args) throws Exception {
+ int count = Integer.parseInt(args[0]);
+ ReplayCache cache = ReplayCache.getInstance("dfl:./");
+ AuthTimeWithHash a1 =
+ new AuthTimeWithHash(client, server, time(-400), 0, hash("1"));
+ AuthTimeWithHash a2 =
+ new AuthTimeWithHash(client, server, time(0), 0, 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<count; i++) {
+ a1 = new AuthTimeWithHash(client, server, time(-400), 0, hash(""));
+ cache.checkAndStore(then, a1);
+ }
+
+ // Now, we add a new one. If some conditions hold, the old ones
+ // will be expunged.
+ cache.checkAndStore(now, a2);
+
+ // and adding an old one will not trigger any error
+ cache.checkAndStore(now, a1);
+ }
+
+ private static String hash(String s) {
+ char[] data = new char[16];
+ for (int i=0; i<16; i++) {
+ if (i < s.length()) data[i] = s.charAt(i);
+ else data[i] = (char)('0' + rand.nextInt(10));
+ }
+ return new String(data);
+ }
+
+ private static int time(int x) {
+ return (int)(System.currentTimeMillis()/1000) + x;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/ReplayCachePrecise.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 8001326
+ * @run main/othervm ReplayCachePrecise
+ * @summary when there are 2 two AuthTime with the same time but different hash,
+ * it's not a replay.
+*/
+
+import java.nio.ByteBuffer;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardOpenOption;
+import java.util.Random;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.internal.KerberosTime;
+import sun.security.krb5.internal.ReplayCache;
+import sun.security.krb5.internal.rcache.AuthTimeWithHash;
+
+public class ReplayCachePrecise {
+ static final String client = "dummy@REALM";
+ static final String server = "server/localhost@REALM";
+ static final Random rand = new Random();
+
+ public static void main(String[] args) throws Exception {
+
+ AuthTimeWithHash a1 = new AuthTimeWithHash(client, server, time(0), 0,
+ "1111111111111111");
+ AuthTimeWithHash a2 = new AuthTimeWithHash(client, server, time(0), 0,
+ "2222222222222222");
+ KerberosTime now = new KerberosTime(time(0)*1000L);
+
+ // When all new styles, must exact match
+ ReplayCache cache = ReplayCache.getInstance("dfl:./c1");
+ cache.checkAndStore(now, a1);
+ cache.checkAndStore(now, a2);
+
+ // When only old style in cache, partial match
+ cache = ReplayCache.getInstance("dfl:./c2");
+ cache.checkAndStore(now, a1);
+ // A small surgery to remove the new style from the cache file
+ SeekableByteChannel ch = Files.newByteChannel(Paths.get("c2"),
+ StandardOpenOption.WRITE,
+ StandardOpenOption.READ);
+ ch.position(6);
+ ch.write(ByteBuffer.wrap(a1.encode(false)));
+ ch.truncate(ch.position());
+ ch.close();
+ try {
+ cache.checkAndStore(now, a2);
+ throw new Exception();
+ } catch (KrbException ke) {
+ // Correct
+ System.out.println(ke);
+ }
+ }
+
+ private static int time(int x) {
+ return (int)(System.currentTimeMillis()/1000) + x;
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/ReplayCacheTest.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2012, 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 7118809 8001326
+ * @run main/othervm ReplayCacheTest jvm
+ * @run main/othervm ReplayCacheTest dfl
+ * @summary rcache deadlock
+ */
+
+import java.io.File;
+import org.ietf.jgss.GSSException;
+import sun.security.jgss.GSSUtil;
+import sun.security.krb5.KrbException;
+import sun.security.krb5.internal.Krb5;
+
+public class ReplayCacheTest {
+
+ public static void main(String[] args)
+ throws Exception {
+
+ new OneKDC(null);
+
+ if (args[0].equals("dfl")) {
+ // Store file in scratch directory
+ args[0] = "dfl:" + System.getProperty("user.dir") + File.separator;
+ System.setProperty("sun.security.krb5.rcache", args[0]);
+ }
+
+ Context c, s;
+ c = Context.fromUserPass(OneKDC.USER, OneKDC.PASS, false);
+ s = Context.fromUserKtab(OneKDC.SERVER, OneKDC.KTAB, true);
+
+ c.startAsClient(OneKDC.SERVER, GSSUtil.GSS_KRB5_MECH_OID);
+ s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+
+ byte[] first = c.take(new byte[0]);
+ c.take(s.take(first));
+
+ s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+ try {
+ s.take(first); // Replay the last apreq sent
+ throw new Exception("This method should fail");
+ } catch (GSSException gsse) {
+ gsse.printStackTrace();
+ KrbException ke = (KrbException)gsse.getCause();
+ if (ke.returnCode() != Krb5.KRB_AP_ERR_REPEAT) {
+ throw gsse;
+ }
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/security/krb5/auto/ReplayCacheTestProc.java Fri Jun 21 18:26:13 2013 +0800
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 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.
+ *
+ * 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 7152176
+ * @summary More krb5 tests
+ * @library ../../../../java/security/testlibrary/
+ * @compile -XDignore.symbol.file ReplayCacheTestProc.java
+ * @run main/othervm/timeout=100 ReplayCacheTestProc
+ */
+
+import java.io.*;
+import java.nio.BufferUnderflowException;
+import java.nio.channels.SeekableByteChannel;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+import java.nio.file.StandardOpenOption;
+import java.security.MessageDigest;
+import java.util.*;
+import sun.security.jgss.GSSUtil;
+import sun.security.krb5.internal.APReq;
+import sun.security.krb5.internal.rcache.AuthTime;
+
+// This test runs multiple acceptor Procs to mimin AP-REQ replays.
+public class ReplayCacheTestProc {
+
+ private static Proc[] ps;
+ private static Proc pc;
+ private static List<Req> reqs = new ArrayList<>();
+ private static String HOST = "localhost";
+
+ // Where should the rcache be saved. It seems KRB5RCACHEDIR is not
+ // recognized on Solaris. Maybe version too low? I see 1.6.
+ private static String cwd =
+ System.getProperty("os.name").startsWith("SunOS") ?
+ "/var/krb5/rcache/" :
+ System.getProperty("user.dir");
+
+
+ private static int uid;
+
+ 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
+
+ try {
+ Class<?> clazz = Class.forName(
+ "com.sun.security.auth.module.UnixSystem");
+ uid = (int)(long)(Long)
+ clazz.getMethod("getUid").invoke(clazz.newInstance());
+ } catch (Exception e) {
+ uid = -1;
+ }
+
+ KDC kdc = KDC.create(OneKDC.REALM, HOST, 0, true);
+ for (int i=0; i<nu; i++) {
+ kdc.addPrincipal(user(i), OneKDC.PASS);
+ }
+ kdc.addPrincipalRandKey("krbtgt/" + OneKDC.REALM);
+ for (int i=0; i<np; i++) {
+ kdc.addPrincipalRandKey(peer(i));
+ }
+
+ kdc.writeKtab(OneKDC.KTAB);
+ KDC.saveConfig(OneKDC.KRB5_CONF, kdc);
+
+ pc = Proc.create("ReplayCacheTestProc").debug("C")
+ .args("client")
+ .start();
+ ps = new Proc[ns];
+ Ex[] result = new Ex[nx];
+
+ if (!random) {
+ // 2 experiments, 2 server, 1 peer, 1 user
+ nx = 2; ns = 2; np = 1; nu = 1;
+
+ // Creates reqs from user# to peer#
+ req(0, 0);
+
+ // Creates server#
+ ps[0] = ns(0);
+ ps[1] = js(1);
+
+ // Runs ex# using req# to server# with expected result
+ result[0] = round(0, 0, 0, true);
+ result[1] = round(1, 0, 1, false);
+ } else {
+ Random r = new Random();
+ for (int i=0; i<ns; i++) {
+ boolean useNative = (mode == 1) ? true
+ : (mode == -1 ? false : r.nextBoolean());
+ ps[i] = useNative?ns(i):js(i);
+ }
+ for (int i=0; i<nx; i++) {
+ result[i] = new Ex();
+ int old; // which req to send
+ boolean expected;
+ if (reqs.isEmpty() || r.nextBoolean()) {
+ Proc.d("Console get new AP-REQ");
+ old = req(r.nextInt(nu), r.nextInt(np));
+ expected = true;
+ } else {
+ Proc.d("Console resue old");
+ old = r.nextInt(reqs.size());
+ expected = false;
+ }
+ int s = r.nextInt(ns);
+ Proc.d("Console send to " + s);
+ result[i] = round(i, old, s, expected);
+ Proc.d("Console sees " + result[i].actual);
+ }
+ }
+
+ pc.println("END");
+ for (int i=0; i<ns; i++) {
+ ps[i].println("END");
+ }
+ System.out.println("Result\n======");
+ boolean finalOut = true;
+ for (int i=0; i<nx; i++) {
+ boolean out = result[i].expected==result[i].actual;
+ finalOut &= out;
+ System.out.printf("%3d: %s (%2d): u%d h%d %s %s %s %2d\n",
+ i,
+ result[i].expected?"----":" ",
+ result[i].old,
+ result[i].user, result[i].peer, result[i].server,
+ result[i].actual?"Good":"Bad ",
+ out?" ":"xxx",
+ result[i].csize);
+ }
+ if (!finalOut) throw new Exception();
+ } else if (args[0].equals("client")) {
+ while (true) {
+ String title = Proc.textIn();
+ Proc.d("Client see " + title);
+ if (title.equals("END")) break;
+ String[] cas = title.split(" ");
+ Context c = Context.fromUserPass(cas[0], OneKDC.PASS, false);
+ c.startAsClient(cas[1], GSSUtil.GSS_KRB5_MECH_OID);
+ c.x().requestCredDeleg(true);
+ byte[] token = c.take(new byte[0]);
+ Proc.d("Client AP-REQ generated");
+ Proc.binOut(token);
+ }
+ } else {
+ Proc.d("Server start");
+ Context s = Context.fromUserKtab("*", OneKDC.KTAB, true);
+ Proc.d("Server login");
+ while (true) {
+ String title = Proc.textIn();
+ Proc.d("Server " + args[0] + " sees " + title);
+ if (title.equals("END")) break;
+ s.startAsServer(GSSUtil.GSS_KRB5_MECH_OID);
+ byte[] token = Proc.binIn();
+ try {
+ s.take(token);
+ Proc.textOut("true");
+ Proc.d(args[0] + " Good");
+ } catch (Exception e) {
+ Proc.textOut("false");
+ Proc.d(args[0] + " Bad");
+ }
+ }
+ }
+ }
+
+ public static void main(String[] args) throws Exception {
+ try {
+ main0(args);
+ } catch (Exception e) {
+ Proc.d(e);
+ throw e;
+ }
+ }
+
+ // returns the user name
+ private static String user(int p) {
+ return "USER" + p;
+ }
+ // returns the peer name
+ private static String peer(int p) {
+ return "host" + p + "/" + HOST;
+ }
+ // returns the dfl name for a host
+ private static String dfl(int p) {
+ return cwd + "host" + p + (uid == -1 ? "" : ("_"+uid));
+ }
+ // generates an ap-req and save into reqs, returns the index
+ private static int req(int user, int peer) throws Exception {
+ pc.println(user(user) + " " + peer(peer));
+ Req req = new Req(user, peer, pc.readData());
+ reqs.add(req);
+ return reqs.size() - 1;
+ }
+ // carries out a round of experiment
+ // i: ex#, old: which req, server: which server, expected: result?
+ private static Ex round(int i, int old, int server, boolean expected)
+ throws Exception {
+ ps[server].println("TEST");
+ ps[server].println(reqs.get(old).msg);
+ String reply = ps[server].readData();
+ Ex result = new Ex();
+ result.i = i;
+ result.expected = expected;
+ result.server = ps[server].debug();
+ result.actual = Boolean.valueOf(reply);
+ result.user = reqs.get(old).user;
+ result.peer = reqs.get(old).peer;
+ result.old = old;
+ result.csize = csize(result.peer);
+ result.hash = hash(reqs.get(old).msg);
+ if (new File(dfl(result.peer)).exists()) {
+ Files.copy(Paths.get(dfl(result.peer)), Paths.get(
+ String.format("%03d-USER%d-host%d-%s-%s",
+ i, result.user, result.peer, result.server,
+ result.actual)
+ + "-" + result.hash),
+ StandardCopyOption.COPY_ATTRIBUTES);
+ }
+ return result;
+ }
+ // create a native server
+ private static Proc ns(int i) throws Exception {
+ return Proc.create("ReplayCacheTestProc")
+ .args("N"+i)
+ .env("KRB5_CONFIG", OneKDC.KRB5_CONF)
+ .env("KRB5_KTNAME", OneKDC.KTAB)
+ .env("KRB5RCACHEDIR", cwd)
+ .prop("sun.security.jgss.native", "true")
+ .prop("javax.security.auth.useSubjectCredsOnly", "false")
+ .prop("sun.security.nativegss.debug", "true")
+ .debug("N"+i)
+ .start();
+ }
+ // creates a java server
+ private static Proc js(int i) throws Exception {
+ return Proc.create("ReplayCacheTestProc")
+ .debug("S"+i)
+ .args("S"+i)
+ .prop("sun.security.krb5.rcache", "dfl")
+ .prop("java.io.tmpdir", cwd)
+ .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());
+ char[] h = new char[hash.length * 2];
+ char[] hexConst = "0123456789ABCDEF".toCharArray();
+ for (int i=0; i<hash.length; i++) {
+ h[2*i] = hexConst[(hash[i]&0xff)>>4];
+ h[2*i+1] = hexConst[hash[i]&0xf];
+ }
+ return new String(h);
+ }
+ // return size of dfl file, excluding the null hash ones
+ private static int csize(int p) throws Exception {
+ try (SeekableByteChannel chan = Files.newByteChannel(
+ Paths.get(dfl(p)), StandardOpenOption.READ)) {
+ chan.position(6);
+ int cc = 0;
+ while (true) {
+ try {
+ if (AuthTime.readFrom(chan) != null) cc++;
+ } catch (BufferUnderflowException e) {
+ break;
+ }
+ }
+ return cc;
+ } catch (IOException ioe) {
+ return 0;
+ }
+ }
+ // models an experiement
+ private static class Ex {
+ int i; // #
+ boolean expected; // expected result
+ boolean actual; // actual output
+ int old; // which ap-req to send
+ String server; // which server to send to
+ String hash; // the hash of req
+ int user; // which initiator
+ int peer; // which acceptor
+ int csize; // size of rcache after test
+ }
+ // models a saved ap-req msg
+ private static class Req {
+ String msg; // based64-ed req
+ int user; // which initiator
+ int peer; // which accceptor
+ Req(int user, int peer, String msg) {
+ this.msg = msg;
+ this.user= user;
+ this.peer = peer;
+ }
+ }
+}
--- a/jdk/test/sun/security/krb5/ccache/EmptyCC.java Thu Jun 20 19:14:30 2013 +0100
+++ b/jdk/test/sun/security/krb5/ccache/EmptyCC.java Fri Jun 21 18:26:13 2013 +0800
@@ -26,15 +26,12 @@
* @bug 7158329
* @bug 8001208
* @summary NPE in sun.security.krb5.Credentials.acquireDefaultCreds()
+ * @library ../../../../java/security/testlibrary/
* @compile -XDignore.symbol.file EmptyCC.java
* @run main EmptyCC tmpcc
* @run main EmptyCC FILE:tmpcc
*/
import java.io.File;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
import sun.security.krb5.Credentials;
import sun.security.krb5.PrincipalName;
import sun.security.krb5.internal.ccache.CredentialsCache;
@@ -48,32 +45,9 @@
// Main process, write the ccache and launch sub process
CredentialsCache cache = CredentialsCache.create(pn, ccache);
cache.save();
-
- // java -cp $test.classes EmptyCC readcc
- ProcessBuilder pb = new ProcessBuilder(
- new File(new File(System.getProperty("java.home"), "bin"),
- "java").getPath(),
- "-cp",
- System.getProperty("test.classes"),
- "EmptyCC",
- ccache,
- "readcc"
- );
-
- pb.environment().put("KRB5CCNAME", ccache);
- pb.redirectErrorStream(true);
-
- Process p = pb.start();
- try (InputStream ins = p.getInputStream()) {
- byte[] buf = new byte[8192];
- int n;
- while ((n = ins.read(buf)) > 0) {
- System.out.write(buf, 0, n);
- }
- }
- if (p.waitFor() != 0) {
- throw new Exception("Test failed");
- }
+ Proc p = Proc.create("EmptyCC").args(ccache, "readcc")
+ .env("KRB5CCNAME", ccache).start();
+ p.waitFor();
} else {
// Sub process, read the ccache
String cc = System.getenv("KRB5CCNAME");