feat(jdk8): move files to new folder to avoid resources compiled.
This commit is contained in:
151
jdkSrc/jdk8/sun/security/krb5/internal/rcache/AuthList.java
Normal file
151
jdkSrc/jdk8/sun/security/krb5/internal/rcache/AuthList.java
Normal file
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2000, 2018, 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 {@link 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;
|
||||
|
||||
// entries.getLast().ctime, updated after each cleanup.
|
||||
private volatile int oldestTime = Integer.MIN_VALUE;
|
||||
|
||||
/**
|
||||
* 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 synchronized void put(AuthTimeWithHash t, KerberosTime currentTime)
|
||||
throws KrbApErrException {
|
||||
|
||||
if (entries.isEmpty()) {
|
||||
entries.addFirst(t);
|
||||
oldestTime = t.ctime;
|
||||
return;
|
||||
} 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;
|
||||
|
||||
// Only trigger a cleanup when the earliest entry is
|
||||
// lifespan + 5 sec ago. This ensures a cleanup is done
|
||||
// at most every 5 seconds so that we don't always
|
||||
// addLast(removeLast).
|
||||
if (oldestTime > timeLimit - 5) {
|
||||
return;
|
||||
}
|
||||
|
||||
// and we remove the *enough* old ones (1 lifetime ago)
|
||||
while (!entries.isEmpty()) {
|
||||
AuthTimeWithHash removed = entries.removeLast();
|
||||
if (removed.ctime >= timeLimit) {
|
||||
entries.addLast(removed);
|
||||
oldestTime = removed.ctime;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
oldestTime = Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
156
jdkSrc/jdk8/sun/security/krb5/internal/rcache/AuthTime.java
Normal file
156
jdkSrc/jdk8/sun/security/krb5/internal/rcache/AuthTime.java
Normal file
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.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 an old style replay cache entry. It is only used in
|
||||
* a dfl file.
|
||||
*
|
||||
* @author Sun/Oracle
|
||||
* @author Yanni Zhang
|
||||
*/
|
||||
public class AuthTime {
|
||||
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
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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");
|
||||
}
|
||||
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, hashAlg, hash);
|
||||
} else {
|
||||
return new AuthTime(
|
||||
client, server, ctime, cusec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Encodes to be used in a dfl file
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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 sun.security.action.GetPropertyAction;
|
||||
|
||||
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> {
|
||||
|
||||
// The hash algorithm can be "HASH" or "SHA256".
|
||||
public static final String DEFAULT_HASH_ALG;
|
||||
|
||||
static {
|
||||
if (GetPropertyAction.privilegedGetProperty(
|
||||
"jdk.krb5.rcache.useMD5", "false").equals("true")) {
|
||||
DEFAULT_HASH_ALG = "HASH";
|
||||
} else {
|
||||
DEFAULT_HASH_ALG = "SHA256";
|
||||
}
|
||||
}
|
||||
|
||||
public static String realAlg(String alg) {
|
||||
switch (alg) {
|
||||
case "HASH":
|
||||
return "MD5";
|
||||
case "SHA256":
|
||||
return "SHA-256";
|
||||
default:
|
||||
throw new AssertionError(alg + " is not HASH or SHA256");
|
||||
}
|
||||
}
|
||||
|
||||
final String hashAlg;
|
||||
final String hash;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>AuthTimeWithHash</code>.
|
||||
*/
|
||||
public AuthTimeWithHash(String client, String server,
|
||||
int ctime, int cusec, String hashAlg, String hash) {
|
||||
super(client, server, ctime, cusec);
|
||||
this.hashAlg = hashAlg;
|
||||
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(hashAlg, that.hashAlg)
|
||||
&& 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 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) {
|
||||
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("%s:%s %d:%s %d:%s", hashAlg, hash,
|
||||
client.length(), client,
|
||||
server.length(), server);
|
||||
} else {
|
||||
cstring = client;
|
||||
sstring = server;
|
||||
}
|
||||
return encode0(cstring, sstring);
|
||||
}
|
||||
}
|
||||
367
jdkSrc/jdk8/sun/security/krb5/internal/rcache/DflCache.java
Normal file
367
jdkSrc/jdk8/sun/security/krb5/internal/rcache/DflCache.java
Normal file
@@ -0,0 +1,367 @@
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Update: New version can use other hash algorithms.
|
||||
*/
|
||||
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.sameTimeDiffHash((AuthTimeWithHash)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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (c) 2013, 2018, 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.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
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 ConcurrentHashMap<>();
|
||||
|
||||
@Override
|
||||
public synchronized void checkAndStore(KerberosTime currTime, AuthTimeWithHash time)
|
||||
throws KrbApErrException {
|
||||
String key = time.client + "|" + time.server;
|
||||
content.computeIfAbsent(key, k -> new AuthList(lifespan))
|
||||
.put(time, currTime);
|
||||
if (DEBUG) {
|
||||
System.out.println("MemoryCache: add " + time + " to " + key);
|
||||
}
|
||||
// TODO: clean up AuthList entries with only expired AuthTimeWithHash objects.
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (AuthList rc: content.values()) {
|
||||
sb.append(rc.toString());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user