* InboundMessageDistributor:

- Don't discard an encrypted DSRM received
     down a tunnel, just strip the hashes like we do for unencrypted
   - Send a store of our own encrypted LS received down a tunnel to
     the InNetMessagePool so the FloodfillVerifyStoreJob will see it.
 * NetDB: Fix LS store verifies with encrypted replies
   by storing the tagset with the correct SKM for the inbound tunnel used.
   Broken since 0.9.7 when it was introduced.
This commit is contained in:
zzz
2013-10-29 20:57:00 +00:00
parent 6afd2c4b97
commit 91ef3fd0bc
3 changed files with 111 additions and 38 deletions

View File

@@ -82,7 +82,21 @@ class FloodfillVerifyStoreJob extends JobImpl {
return;
}
DatabaseLookupMessage lookup = buildLookup();
boolean isInboundExploratory;
TunnelInfo replyTunnelInfo;
if (_isRouterInfo || getContext().keyRing().get(_key) != null) {
replyTunnelInfo = getContext().tunnelManager().selectInboundExploratoryTunnel(_target);
isInboundExploratory = true;
} else {
replyTunnelInfo = getContext().tunnelManager().selectInboundTunnel(_key, _target);
isInboundExploratory = false;
}
if (replyTunnelInfo == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("No inbound tunnels to get a reply from!");
return;
}
DatabaseLookupMessage lookup = buildLookup(replyTunnelInfo);
if (lookup == null) {
_facade.verifyFinished(_key);
return;
@@ -112,7 +126,19 @@ class FloodfillVerifyStoreJob extends JobImpl {
return;
}
if (DatabaseLookupMessage.supportsEncryptedReplies(peer)) {
MessageWrapper.OneTimeSession sess = MessageWrapper.generateSession(getContext());
// register the session with the right SKM
MessageWrapper.OneTimeSession sess;
if (isInboundExploratory) {
sess = MessageWrapper.generateSession(getContext());
} else {
sess = MessageWrapper.generateSession(getContext(), _key);
if (sess == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("No SKM to reply to");
_facade.verifyFinished(_key);
return;
}
}
if (_log.shouldLog(Log.INFO))
_log.info("Requesting encrypted reply from " + _target + ' ' + sess.key + ' ' + sess.tag);
lookup.setReplySession(sess.key, sess.tag);
@@ -154,20 +180,10 @@ class FloodfillVerifyStoreJob extends JobImpl {
return _sentTo;
}
private DatabaseLookupMessage buildLookup() {
private DatabaseLookupMessage buildLookup(TunnelInfo replyTunnelInfo) {
// If we are verifying a leaseset, use the destination's own tunnels,
// to avoid association by the exploratory tunnel OBEP.
// Unless it is an encrypted leaseset.
TunnelInfo replyTunnelInfo;
if (_isRouterInfo || getContext().keyRing().get(_key) != null)
replyTunnelInfo = getContext().tunnelManager().selectInboundExploratoryTunnel(_target);
else
replyTunnelInfo = getContext().tunnelManager().selectInboundTunnel(_key, _target);
if (replyTunnelInfo == null) {
if (_log.shouldLog(Log.WARN))
_log.warn("No inbound tunnels to get a reply from!");
return null;
}
DatabaseLookupMessage m = new DatabaseLookupMessage(getContext(), true);
m.setMessageExpiration(getContext().clock().now() + VERIFY_TIMEOUT);
m.setReplyTunnel(replyTunnelInfo.getReceiveTunnelId(0));

View File

@@ -168,10 +168,38 @@ public class MessageWrapper {
* @since 0.9.7
*/
public static OneTimeSession generateSession(RouterContext ctx) {
return generateSession(ctx, ctx.sessionKeyManager());
}
/**
* Create a single key and tag, for receiving a single encrypted message,
* and register it with the client's session key manager, to expire in two minutes.
* The recipient can then send us an AES-encrypted message,
* avoiding ElGamal.
*
* @return null if we can't find the SKM for the localDest
* @since 0.9.9
*/
public static OneTimeSession generateSession(RouterContext ctx, Hash localDest) {
SessionKeyManager skm = ctx.clientManager().getClientSessionKeyManager(localDest);
if (skm == null)
return null;
return generateSession(ctx, skm);
}
/**
* Create a single key and tag, for receiving a single encrypted message,
* and register it with the given session key manager, to expire in two minutes.
* The recipient can then send us an AES-encrypted message,
* avoiding ElGamal.
*
* @since 0.9.9
*/
private static OneTimeSession generateSession(RouterContext ctx, SessionKeyManager skm) {
SessionKey key = ctx.keyGenerator().generateSessionKey();
SessionTag tag = new SessionTag(true);
Set<SessionTag> tags = new RemovableSingletonSet(tag);
ctx.sessionKeyManager().tagsReceived(key, tags, 2*60*1000);
skm.tagsReceived(key, tags, 2*60*1000);
return new OneTimeSession(key, tag);
}

View File

@@ -62,13 +62,15 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
if ( (_client != null) &&
(type == DatabaseSearchReplyMessage.MESSAGE_TYPE) &&
(_client.equals(((DatabaseSearchReplyMessage)msg).getSearchKey()))) {
if (_log.shouldLog(Log.WARN))
_log.warn("Removing replies from a DSRM down a tunnel for " + _client + ": " + msg);
DatabaseSearchReplyMessage orig = (DatabaseSearchReplyMessage) msg;
DatabaseSearchReplyMessage newMsg = new DatabaseSearchReplyMessage(_context);
newMsg.setFromHash(orig.getFromHash());
newMsg.setSearchKey(orig.getSearchKey());
msg = newMsg;
if (orig.getNumReplies() > 0) {
if (_log.shouldLog(Log.WARN))
_log.warn("Removing replies from a DSRM down a tunnel for " + _client + ": " + msg);
DatabaseSearchReplyMessage newMsg = new DatabaseSearchReplyMessage(_context);
newMsg.setFromHash(orig.getFromHash());
newMsg.setSearchKey(orig.getSearchKey());
msg = newMsg;
}
} else if ( (_client != null) &&
(type == DatabaseStoreMessage.MESSAGE_TYPE) &&
(((DatabaseStoreMessage)msg).getEntry().getType() == DatabaseEntry.KEY_TYPE_ROUTERINFO)) {
@@ -156,29 +158,43 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
case DeliveryInstructions.DELIVERY_MODE_LOCAL:
if (_log.shouldLog(Log.DEBUG))
_log.debug("local delivery instructions for clove: " + data.getClass().getName());
if (data instanceof GarlicMessage) {
int type = data.getType();
if (type == GarlicMessage.MESSAGE_TYPE) {
_receiver.receive((GarlicMessage)data);
} else if (data instanceof DatabaseStoreMessage) {
} else if (type == DatabaseStoreMessage.MESSAGE_TYPE) {
// Treat db store explicitly here (not in HandleFloodfillDatabaseStoreMessageJob),
// since we don't want to republish (or flood)
// unnecessarily. Reply tokens ignored.
DatabaseStoreMessage dsm = (DatabaseStoreMessage)data;
try {
if (dsm.getEntry().getType() == DatabaseEntry.KEY_TYPE_LEASESET) {
// If it was stored to us before, don't undo the
// receivedAsPublished flag so we will continue to respond to requests
// for the leaseset. That is, we don't want this to change the
// RAP flag of the leaseset.
// When the keyspace rotates at midnight, and this leaseset moves out
// of our keyspace, maybe we shouldn't do this?
// Should we do this whether ff or not?
LeaseSet ls = (LeaseSet) dsm.getEntry();
LeaseSet old = _context.netDb().store(dsm.getKey(), ls);
if (old != null && old.getReceivedAsPublished()
/** && ((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled() **/ )
ls.setReceivedAsPublished(true);
if (_log.shouldLog(Log.INFO))
_log.info("Storing LS for: " + dsm.getKey() + " sent to: " + _client);
if (dsm.getKey().equals(_client)) {
// store of our own LS.
// This is almost certainly a response to a FloodfillVerifyStoreJob search.
// We must send to the InNetMessagePool so the message can be matched
// and the verify marked as successful.
// Ensure the reply info is clear...
dsm.setReplyToken(0);
dsm.setReplyTunnel(null);
dsm.setReplyGateway(null);
// ... and inject it.
_context.inNetMessagePool().add(dsm, null, null);
} else {
// If it was stored to us before, don't undo the
// receivedAsPublished flag so we will continue to respond to requests
// for the leaseset. That is, we don't want this to change the
// RAP flag of the leaseset.
// When the keyspace rotates at midnight, and this leaseset moves out
// of our keyspace, maybe we shouldn't do this?
// Should we do this whether ff or not?
LeaseSet ls = (LeaseSet) dsm.getEntry();
LeaseSet old = _context.netDb().store(dsm.getKey(), ls);
if (old != null && old.getReceivedAsPublished()
/** && ((FloodfillNetworkDatabaseFacade)_context.netDb()).floodfillEnabled() **/ )
ls.setReceivedAsPublished(true);
if (_log.shouldLog(Log.INFO))
_log.info("Storing LS for: " + dsm.getKey() + " sent to: " + _client);
}
} else {
if (_client != null) {
// drop it, since the data we receive shouldn't include router
@@ -195,13 +211,26 @@ class InboundMessageDistributor implements GarlicMessageReceiver.CloveReceiver {
if (_log.shouldLog(Log.WARN))
_log.warn("Bad store attempt", iae);
}
} else if (data instanceof DataMessage) {
} else if (_client != null && type == DatabaseSearchReplyMessage.MESSAGE_TYPE &&
_client.equals(((DatabaseSearchReplyMessage) data).getSearchKey())) {
// DSRMs show up here now that replies are encrypted
DatabaseSearchReplyMessage orig = (DatabaseSearchReplyMessage) data;
if (orig.getNumReplies() > 0) {
if (_log.shouldLog(Log.WARN))
_log.warn("Removing replies from a garlic DSRM down a tunnel for " + _client + ": " + data);
DatabaseSearchReplyMessage newMsg = new DatabaseSearchReplyMessage(_context);
newMsg.setFromHash(orig.getFromHash());
newMsg.setSearchKey(orig.getSearchKey());
orig = newMsg;
}
_context.inNetMessagePool().add(orig, null, null);
} else if (type == DataMessage.MESSAGE_TYPE) {
// a data message targetting the local router is how we send load tests (real
// data messages target destinations)
_context.statManager().addRateData("tunnel.handleLoadClove", 1, 0);
data = null;
//_context.inNetMessagePool().add(data, null, null);
} else if (_client != null && data.getType() != DeliveryStatusMessage.MESSAGE_TYPE) {
} else if (_client != null && type != DeliveryStatusMessage.MESSAGE_TYPE) {
// drop it, since the data we receive shouldn't include other stuff,
// as that might open an attack vector
_context.statManager().addRateData("tunnel.dropDangerousClientTunnelMessage", 1,