提交 99fe80ea authored 作者: noelgrandin's avatar noelgrandin

Use HMAC for authenticating remote LOB id's, removing the need for maintaining a…

Use HMAC for authenticating remote LOB id's, removing the need for maintaining a cache, and removing the limit on the number of LOBs per result set.
上级 a680461e
......@@ -23,6 +23,8 @@ Change Log
</li><li>Apache Tomcat 7.x will now longer log a warning when unloading the web application, if using a connection pool.
</li><li>H2 Console: support the Midori browser (for Debian / Raspberry Pi)
</li><li>When opening a remote session, don't open a temporary file if the trace level is set to zero
</li><li>Use HMAC for authenticating remote LOB id's, removing the need for maintaining a cache, and removing the limit
on the number of LOBs per result set.
</li></ul>
<h2>Version 1.3.167 (2012-05-23)</h2>
......
......@@ -235,7 +235,7 @@ abstract class ScriptBase extends Prepared implements DataHandler {
return null;
}
public int readLob(long lobId, long offset, byte[] buff, int off, int length) {
public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) {
throw DbException.throwInternalError();
}
......
......@@ -71,6 +71,11 @@ public class Constants {
*/
public static final int TCP_PROTOCOL_VERSION_11 = 11;
/**
* The TCP protocol version number 11.
*/
public static final int TCP_PROTOCOL_VERSION_12 = 12;
/**
* The major version of this database.
*/
......
......@@ -2401,7 +2401,7 @@ public class Database implements DataHandler {
return false;
}
public int readLob(long lobId, long offset, byte[] buff, int off, int length) {
public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) {
throw DbException.throwInternalError();
}
......
......@@ -96,7 +96,7 @@ public class SessionRemote extends SessionWithState implements DataHandler {
trans.setSSL(ci.isSSL());
trans.init();
trans.writeInt(Constants.TCP_PROTOCOL_VERSION_6);
trans.writeInt(Constants.TCP_PROTOCOL_VERSION_11);
trans.writeInt(Constants.TCP_PROTOCOL_VERSION_12);
trans.writeString(db);
trans.writeString(ci.getOriginalURL());
trans.writeString(ci.getUserName());
......@@ -691,13 +691,14 @@ public class SessionRemote extends SessionWithState implements DataHandler {
return null;
}
public synchronized int readLob(long lobId, long offset, byte[] buff, int off, int length) {
public synchronized int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) {
for (int i = 0, count = 0; i < transferList.size(); i++) {
Transfer transfer = transferList.get(i);
try {
traceOperation("LOB_READ", (int) lobId);
transfer.writeInt(SessionRemote.LOB_READ);
transfer.writeLong(lobId);
transfer.writeBytes(hmac);
transfer.writeLong(offset);
transfer.writeInt(length);
done(transfer);
......
......@@ -37,7 +37,6 @@ import org.h2.util.SmallMap;
import org.h2.util.StringUtils;
import org.h2.value.Transfer;
import org.h2.value.Value;
import org.h2.value.ValueLobDb;
/**
* One server thread is opened per client connection.
......@@ -51,10 +50,7 @@ public class TcpServerThread implements Runnable {
private Thread thread;
private Command commit;
private SmallMap cache = new SmallMap(SysProperties.SERVER_CACHED_OBJECTS);
private SmallLRUCache<Long, CachedInputStream> lobs =
SmallLRUCache.newInstance(Math.max(
SysProperties.SERVER_CACHED_OBJECTS,
SysProperties.SERVER_RESULT_SET_FETCH_SIZE * 5));
private SmallLRUCache<Long, CachedInputStream> lobs = SmallLRUCache.newInstance(SysProperties.SERVER_CACHED_OBJECTS);
private int threadId;
private int clientVersion;
private String sessionId;
......@@ -83,12 +79,12 @@ public class TcpServerThread implements Runnable {
int minClientVersion = transfer.readInt();
if (minClientVersion < Constants.TCP_PROTOCOL_VERSION_6) {
throw DbException.get(ErrorCode.DRIVER_VERSION_ERROR_2, "" + clientVersion, "" + Constants.TCP_PROTOCOL_VERSION_6);
} else if (minClientVersion > Constants.TCP_PROTOCOL_VERSION_11) {
throw DbException.get(ErrorCode.DRIVER_VERSION_ERROR_2, "" + clientVersion, "" + Constants.TCP_PROTOCOL_VERSION_11);
} else if (minClientVersion > Constants.TCP_PROTOCOL_VERSION_12) {
throw DbException.get(ErrorCode.DRIVER_VERSION_ERROR_2, "" + clientVersion, "" + Constants.TCP_PROTOCOL_VERSION_12);
}
int maxClientVersion = transfer.readInt();
if (maxClientVersion >= Constants.TCP_PROTOCOL_VERSION_11) {
clientVersion = Constants.TCP_PROTOCOL_VERSION_11;
if (maxClientVersion >= Constants.TCP_PROTOCOL_VERSION_12) {
clientVersion = Constants.TCP_PROTOCOL_VERSION_12;
} else {
clientVersion = minClientVersion;
}
......@@ -396,15 +392,18 @@ public class TcpServerThread implements Runnable {
break;
}
case SessionRemote.LOB_READ: {
byte[] hmac = transfer.readBytes();
long lobId = transfer.readLong();
transfer.verifyLobMac(hmac, lobId);
CachedInputStream in = lobs.get(lobId);
if (in == null) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
in = new CachedInputStream(null);
lobs.put(lobId, in);
}
long offset = transfer.readLong();
if (in.getPos() != offset) {
LobStorage lobStorage = session.getDataHandler().getLobStorage();
InputStream lobIn = lobStorage.getInputStream(lobId, -1);
InputStream lobIn = lobStorage.getInputStream(lobId, hmac, -1);
in = new CachedInputStream(lobIn);
lobs.put(lobId, in);
lobIn.skip(offset);
......@@ -439,26 +438,13 @@ public class TcpServerThread implements Runnable {
transfer.writeBoolean(true);
Value[] v = result.currentRow();
for (int i = 0; i < result.getVisibleColumnCount(); i++) {
writeValue(v[i]);
transfer.writeValue(v[i]);
}
} else {
transfer.writeBoolean(false);
}
}
private void writeValue(Value v) throws IOException {
if (v.getType() == Value.CLOB || v.getType() == Value.BLOB) {
if (v instanceof ValueLobDb) {
ValueLobDb lob = (ValueLobDb) v;
if (lob.isStored()) {
long id = lob.getLobId();
lobs.put(id, new CachedInputStream(null));
}
}
}
transfer.writeValue(v);
}
void setThread(Thread thread) {
this.thread = thread;
}
......
......@@ -793,7 +793,7 @@ public class Data {
long lobId = readVarLong();
long precision = readVarLong();
LobStorage lobStorage = handler.getLobStorage();
ValueLobDb lob = ValueLobDb.create(type, lobStorage, tableId, lobId, precision);
ValueLobDb lob = ValueLobDb.create(type, lobStorage, tableId, lobId, null, precision);
return lob;
} else {
int tableId = readVarInt();
......
......@@ -109,6 +109,6 @@ public interface DataHandler {
* @param length the number of bytes to read
* @return the number of bytes read
*/
int readLob(long lobId, long offset, byte[] buff, int off, int length);
int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length);
}
......@@ -225,7 +225,7 @@ public class FileLock implements Runnable {
transfer.setSocket(socket);
transfer.init();
transfer.writeInt(Constants.TCP_PROTOCOL_VERSION_6);
transfer.writeInt(Constants.TCP_PROTOCOL_VERSION_11);
transfer.writeInt(Constants.TCP_PROTOCOL_VERSION_12);
transfer.writeString(null);
transfer.writeString(null);
transfer.writeString(id);
......
......@@ -281,6 +281,8 @@ public class LobStorage {
*/
private final long lob;
private final byte[] hmac;
/**
* The position.
*/
......@@ -291,9 +293,10 @@ public class LobStorage {
*/
private long remainingBytes;
public RemoteInputStream(DataHandler handler, long lob, long byteCount) {
public RemoteInputStream(DataHandler handler, long lob, byte[] hmac, long byteCount) {
this.handler = handler;
this.lob = lob;
this.hmac = hmac;
remainingBytes = byteCount;
}
......@@ -315,7 +318,7 @@ public class LobStorage {
if (length == 0) {
return -1;
}
length = handler.readLob(lob, pos, buff, off, length);
length = handler.readLob(lob, hmac, pos, buff, off, length);
remainingBytes -= length;
if (length == 0) {
return -1;
......@@ -547,13 +550,13 @@ public class LobStorage {
* @param byteCount the number of bytes to read, or -1 if not known
* @return the stream
*/
public InputStream getInputStream(long lobId, long byteCount) throws IOException {
public InputStream getInputStream(long lobId, byte[] hmac, long byteCount) throws IOException {
init();
if (conn == null) {
if (byteCount < 0) {
byteCount = Long.MAX_VALUE;
}
return new BufferedInputStream(new RemoteInputStream(handler, lobId, byteCount));
return new BufferedInputStream(new RemoteInputStream(handler, lobId, hmac, byteCount));
}
if (byteCount == -1) {
synchronized (handler) {
......@@ -644,7 +647,7 @@ public class LobStorage {
prep.setInt(3, tableId);
prep.execute();
reuse(sql, prep);
ValueLobDb v = ValueLobDb.create(type, this, tableId, lobId, byteCount);
ValueLobDb v = ValueLobDb.create(type, this, tableId, lobId, null, byteCount);
return v;
} catch (SQLException e) {
throw DbException.convert(e);
......@@ -683,7 +686,7 @@ public class LobStorage {
prep.executeUpdate();
reuse(sql, prep);
ValueLobDb v = ValueLobDb.create(type, this, tableId, lobId, length);
ValueLobDb v = ValueLobDb.create(type, this, tableId, lobId, null, length);
return v;
} catch (SQLException e) {
throw DbException.convert(e);
......
......@@ -193,7 +193,7 @@ public class Recover extends Tool implements DataHandler {
public static Value.ValueBlob readBlobDb(Connection conn, long lobId, long precision) {
DataHandler h = ((JdbcConnection) conn).getSession().getDataHandler();
LobStorage lobStorage = h.getLobStorage();
return ValueLobDb.create(Value.BLOB, lobStorage, LobStorage.TABLE_TEMP, lobId, precision);
return ValueLobDb.create(Value.BLOB, lobStorage, LobStorage.TABLE_TEMP, lobId, null, precision);
}
/**
......@@ -202,7 +202,7 @@ public class Recover extends Tool implements DataHandler {
public static Value.ValueClob readClobDb(Connection conn, long lobId, long precision) {
DataHandler h = ((JdbcConnection) conn).getSession().getDataHandler();
LobStorage lobStorage = h.getLobStorage();
return ValueLobDb.create(Value.CLOB, lobStorage, LobStorage.TABLE_TEMP, lobId, precision);
return ValueLobDb.create(Value.CLOB, lobStorage, LobStorage.TABLE_TEMP, lobId, null, precision);
}
private void trace(String message) {
......@@ -1415,7 +1415,7 @@ public class Recover extends Tool implements DataHandler {
/**
* INTERNAL
*/
public int readLob(long lobId, long offset, byte[] buff, int off, int length) {
public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) {
throw DbException.throwInternalError();
}
......
......@@ -26,11 +26,13 @@ import org.h2.engine.Constants;
import org.h2.engine.SessionInterface;
import org.h2.message.DbException;
import org.h2.message.TraceSystem;
import org.h2.security.SHA256;
import org.h2.store.Data;
import org.h2.store.DataReader;
import org.h2.tools.SimpleResultSet;
import org.h2.util.DateTimeUtils;
import org.h2.util.IOUtils;
import org.h2.util.MathUtils;
import org.h2.util.NetUtils;
import org.h2.util.StringUtils;
import org.h2.util.Utils;
......@@ -43,6 +45,7 @@ public class Transfer {
private static final int BUFFER_SIZE = 16 * 1024;
private static final int LOB_MAGIC = 0x1234;
private static final int LOB_MAC_SALT_LENGTH = 16;
private Socket socket;
private DataInputStream in;
......@@ -50,6 +53,7 @@ public class Transfer {
private SessionInterface session;
private boolean ssl;
private int version;
private byte[] lobMacSalt;
/**
* Create a new transfer object for the specified session.
......@@ -405,13 +409,14 @@ public class Transfer {
writeString(v.getString());
break;
case Value.BLOB: {
if (version >= Constants.TCP_PROTOCOL_VERSION_11) {
if (version >= Constants.TCP_PROTOCOL_VERSION_12) {
if (v instanceof ValueLobDb) {
ValueLobDb lob = (ValueLobDb) v;
if (lob.isStored()) {
writeLong(-1);
writeInt(lob.getTableId());
writeLong(lob.getLobId());
writeBytes(calculateLobMac(lob.getLobId()));
writeLong(lob.getPrecision());
break;
}
......@@ -430,12 +435,13 @@ public class Transfer {
break;
}
case Value.CLOB: {
if (version >= Constants.TCP_PROTOCOL_VERSION_11) {
if (version >= Constants.TCP_PROTOCOL_VERSION_12) {
if (v instanceof ValueLobDb) {
ValueLobDb lob = (ValueLobDb) v;
if (lob.isStored()) {
writeLong(-1);
writeInt(lob.getTableId());
writeBytes(calculateLobMac(lob.getLobId()));
writeLong(lob.getLobId());
writeLong(lob.getPrecision());
break;
......@@ -567,12 +573,13 @@ public class Transfer {
return ValueStringFixed.get(readString());
case Value.BLOB: {
long length = readLong();
if (version >= Constants.TCP_PROTOCOL_VERSION_11) {
if (version >= Constants.TCP_PROTOCOL_VERSION_12) {
if (length == -1) {
int tableId = readInt();
long id = readLong();
byte[] hmac = readBytes();
long precision = readLong();
return ValueLobDb.create(Value.BLOB, session.getDataHandler().getLobStorage(), tableId, id, precision);
return ValueLobDb.create(Value.BLOB, session.getDataHandler().getLobStorage(), tableId, id, hmac, precision);
}
int len = (int) length;
byte[] small = new byte[len];
......@@ -592,12 +599,13 @@ public class Transfer {
}
case Value.CLOB: {
long length = readLong();
if (version >= Constants.TCP_PROTOCOL_VERSION_11) {
if (version >= Constants.TCP_PROTOCOL_VERSION_12) {
if (length == -1) {
int tableId = readInt();
long id = readLong();
byte[] hmac = readBytes();
long precision = readLong();
return ValueLobDb.create(Value.CLOB, session.getDataHandler().getLobStorage(), tableId, id, precision);
return ValueLobDb.create(Value.CLOB, session.getDataHandler().getLobStorage(), tableId, id, hmac, precision);
}
DataReader reader = new DataReader(in);
int len = (int) length;
......@@ -703,4 +711,37 @@ public class Transfer {
return socket == null || socket.isClosed();
}
/**
* @throws DbException if the HMAC does not verify
*/
public void verifyLobMac(byte[] hmacData, long lobId) {
byte[] result = calculateLobMac(lobId);
if (!result.equals(hmacData)) {
throw DbException.get(ErrorCode.REMOTE_CONNECTION_NOT_ALLOWED);
}
}
private byte[] calculateLobMac(long lobId) {
if (lobMacSalt == null) {
lobMacSalt = MathUtils.secureRandomBytes(LOB_MAC_SALT_LENGTH);
}
byte[] hmacData = SHA256.getHashWithSalt(longToBytes(lobId), lobMacSalt);
return hmacData;
}
private static byte[] longToBytes(long src) {
byte[] data = new byte[8];
data[0] = (byte) (src >> 56);
data[1] = (byte) (src >> 48);
data[2] = (byte) (src >> 40);
data[3] = (byte) (src >> 32);
data[4] = (byte) (src >> 24);
data[5] = (byte) (src >> 16);
data[6] = (byte) (src >> 8);
data[7] = (byte) src;
return data;
}
}
......@@ -39,6 +39,7 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo
private LobStorage lobStorage;
private long lobId;
private byte[] hmac;
private byte[] small;
......@@ -46,11 +47,12 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo
private FileStore tempFile;
private String fileName;
private ValueLobDb(int type, LobStorage lobStorage, int tableId, long lobId, long precision) {
private ValueLobDb(int type, LobStorage lobStorage, int tableId, long lobId, byte[] hmac, long precision) {
this.type = type;
this.lobStorage = lobStorage;
this.tableId = tableId;
this.lobId = lobId;
this.hmac = hmac;
this.precision = precision;
}
......@@ -71,8 +73,8 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo
* @return the value
*/
public static ValueLobDb create(int type, LobStorage lobStorage,
int tableId, long id, long precision) {
return new ValueLobDb(type, lobStorage, tableId, id, precision);
int tableId, long id, byte[] hmac, long precision) {
return new ValueLobDb(type, lobStorage, tableId, id, hmac, precision);
}
/**
......@@ -285,7 +287,7 @@ public class ValueLobDb extends Value implements Value.ValueClob, Value.ValueBlo
}
long byteCount = (type == Value.BLOB) ? precision : -1;
try {
return lobStorage.getInputStream(lobId, byteCount);
return lobStorage.getInputStream(lobId, hmac, byteCount);
} catch (IOException e) {
throw DbException.convertIOException(e, toString());
}
......
......@@ -323,7 +323,7 @@ public class TestDataPage extends TestBase implements DataHandler {
return null;
}
public int readLob(long lobId, long offset, byte[] buff, int off, int length) {
public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) {
return -1;
}
......
......@@ -175,7 +175,7 @@ public class TestFile extends TestBase implements DataHandler {
return null;
}
public int readLob(long lobId, long offset, byte[] buff, int off, int length) {
public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) {
return -1;
}
......
......@@ -153,7 +153,7 @@ public class TestValueHashMap extends TestBase implements DataHandler {
return null;
}
public int readLob(long lobId, long offset, byte[] buff, int off, int length) {
public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) {
return -1;
}
......
......@@ -257,7 +257,7 @@ public class TestValueMemory extends TestBase implements DataHandler {
return null;
}
public int readLob(long lobId, long offset, byte[] buff, int off, int length) {
public int readLob(long lobId, byte[] hmac, long offset, byte[] buff, int off, int length) {
return -1;
}
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论