diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHandler.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHandler.java
index fa2170c88..4e6dea2ad 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHandler.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHandler.java
@@ -33,6 +33,9 @@ public class ConfigKeyringHandler extends FormHandler {
} catch (DataFormatException dfe) {}
if (h == null || h.getData() == null) {
addFormError(_t("Invalid destination"));
+ } else if (_context.clientManager().isLocal(h)) {
+ // don't bother translating
+ addFormError("Cannot add key for local destination. Enable encryption in the Hidden Services Manager.");
} else if (sk.getData() == null) {
addFormError(_t("Invalid key"));
} else {
@@ -42,12 +45,16 @@ public class ConfigKeyringHandler extends FormHandler {
}
} else { // Delete
if (h != null && h.getData() != null) {
- if (_context.keyRing().remove(h) != null)
+ if (_context.clientManager().isLocal(h)) {
+ // don't bother translating
+ addFormError("Cannot remove key for local destination. Disable encryption in the Hidden Services Manager.");
+ } else if (_context.keyRing().remove(h) != null) {
addFormNotice(_t("Key for {0} removed from keyring",
Base32.encode(h.getData()) + ".b32.i2p"));
- else
+ } else {
addFormNotice(_t("Key for {0} not found in keyring",
Base32.encode(h.getData()) + ".b32.i2p"));
+ }
} else {
addFormError(_t("Invalid destination"));
}
diff --git a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHelper.java b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHelper.java
index 986879291..fcd423bd1 100644
--- a/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHelper.java
+++ b/apps/routerconsole/java/src/net/i2p/router/web/helpers/ConfigKeyringHelper.java
@@ -30,17 +30,31 @@ public class ConfigKeyringHelper extends HelperBase {
*/
private void renderStatusHTML(StringWriter out) throws IOException {
StringBuilder buf = new StringBuilder(1024);
+ buf.append("
").append(_t("Local encrypted destinations")).append("
");
+ render(buf, true);
+ buf.append("").append(_t("Remote encrypted destinations")).append("
");
+ render(buf, false);
+ out.write(buf.toString());
+ out.flush();
+ }
+
+ /**
+ * @since 0.9.33 moved from PersistentKeyRing
+ */
+ private void render(StringBuilder buf, boolean local) {
buf.append("\n| ").append(_t("Destination"))
.append(" | ").append(_t("Name"))
.append(" | ").append(_t("Encryption Key"))
.append(" |
");
for (Map.Entry e : _context.keyRing().entrySet()) {
- buf.append("\n| ");
Hash h = e.getKey();
+ if (local != _context.clientManager().isLocal(h))
+ continue;
+ buf.append("\n |
| ");
buf.append(Base32.encode(h.getData())).append(".b32.i2p");
buf.append(" | ");
Destination dest = _context.netDb().lookupDestinationLocally(h);
- if (dest != null && _context.clientManager().isLocal(dest)) {
+ if (dest != null && local) {
TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(h);
if (in != null && in.getDestinationNickname() != null)
buf.append(in.getDestinationNickname());
@@ -55,7 +69,5 @@ public class ConfigKeyringHelper extends HelperBase {
buf.append(" | \n");
}
buf.append("
\n");
- out.write(buf.toString());
- out.flush();
}
}
diff --git a/core/java/src/net/i2p/client/impl/I2PSessionImpl.java b/core/java/src/net/i2p/client/impl/I2PSessionImpl.java
index c5f7495cc..83931b30e 100644
--- a/core/java/src/net/i2p/client/impl/I2PSessionImpl.java
+++ b/core/java/src/net/i2p/client/impl/I2PSessionImpl.java
@@ -360,6 +360,10 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
session.destroySession();
} catch (I2PSessionException ise) {}
}
+ // do we need this here? subsession.destroySession() calls primary
+ Destination d = session.getMyDestination();
+ if (d != null)
+ _context.keyRing().remove(d.calculateHash());
}
/**
@@ -1210,6 +1214,10 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
private void closeSocket() {
if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Closing the socket", new Exception("closeSocket"));
+ // maybe not the right place for this, but let's be sure
+ Destination d = _myDestination;
+ if (d != null)
+ _context.keyRing().remove(d.calculateHash());
synchronized(_stateLock) {
changeState(State.CLOSING);
locked_closeSocket();
@@ -1217,6 +1225,9 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
}
synchronized (_subsessionLock) {
for (SubSession sess : _subsessions) {
+ d = sess.getMyDestination();
+ if (d != null)
+ _context.keyRing().remove(d.calculateHash());
sess.changeState(State.CLOSED);
sess.setSessionId(null);
sess.setLeaseSet(null);
diff --git a/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java b/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java
index 809492c4c..d88b12e75 100644
--- a/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java
+++ b/core/java/src/net/i2p/client/impl/RequestLeaseSetMessageHandler.java
@@ -12,6 +12,7 @@ package net.i2p.client.impl;
import java.io.EOFException;
import java.security.GeneralSecurityException;
import java.util.Map;
+import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext;
@@ -21,6 +22,7 @@ import net.i2p.crypto.SigType;
import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper;
import net.i2p.data.Destination;
+import net.i2p.data.Hash;
import net.i2p.data.Lease;
import net.i2p.data.LeaseSet;
import net.i2p.data.PrivateKey;
@@ -158,17 +160,27 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
leaseSet.setEncryptionKey(li.getPublicKey());
leaseSet.setSigningKey(li.getSigningPublicKey());
- boolean encrypt = Boolean.parseBoolean(session.getOptions().getProperty("i2cp.encryptLeaseSet"));
- String sk = session.getOptions().getProperty("i2cp.leaseSetKey");
+ // SubSession options aren't updated via the gui, so use the primary options
+ Properties opts;
+ if (session instanceof SubSession)
+ opts = ((SubSession) session).getPrimaryOptions();
+ else
+ opts = session.getOptions();
+ boolean encrypt = Boolean.parseBoolean(opts.getProperty("i2cp.encryptLeaseSet"));
+ String sk = opts.getProperty("i2cp.leaseSetKey");
+ Hash h = dest.calculateHash();
if (encrypt && sk != null) {
SessionKey key = new SessionKey();
try {
key.fromBase64(sk);
leaseSet.encrypt(key);
- _context.keyRing().put(session.getMyDestination().calculateHash(), key);
+ _context.keyRing().put(h, key);
} catch (DataFormatException dfe) {
_log.error("Bad leaseset key: " + sk);
+ _context.keyRing().remove(h);
}
+ } else {
+ _context.keyRing().remove(h);
}
try {
leaseSet.sign(session.getPrivateKey());
diff --git a/core/java/src/net/i2p/client/impl/SubSession.java b/core/java/src/net/i2p/client/impl/SubSession.java
index 6a6706d62..d06e2d798 100644
--- a/core/java/src/net/i2p/client/impl/SubSession.java
+++ b/core/java/src/net/i2p/client/impl/SubSession.java
@@ -85,6 +85,13 @@ class SubSession extends I2PSessionMuxedImpl {
@Override
public void updateOptions(Properties options) {}
+ /**
+ * @since 0.9.33
+ */
+ public Properties getPrimaryOptions() {
+ return _primary.getOptions();
+ }
+
/**
* Connect to the router and establish a session. This call blocks until
* a session is granted.
diff --git a/history.txt b/history.txt
index d51f0746e..1e229ee63 100644
--- a/history.txt
+++ b/history.txt
@@ -1,3 +1,10 @@
+2018-01-14 zzz
+ * Keyring (ticket #2108):
+ - Separate local and remote dests on /configkeyring
+ - Prohibit local changes on /configkeyring
+ - Remove local keys on tunnel shutdown or encryption disable
+ - Ensure subsession encryption setting matches primary session
+
2018-01-13 zzz
* SessionKeyManager: Clean up sessions with excess tagsets
diff --git a/router/java/src/net/i2p/router/PersistentKeyRing.java b/router/java/src/net/i2p/router/PersistentKeyRing.java
index 1f8c0631e..1eeebb4ea 100644
--- a/router/java/src/net/i2p/router/PersistentKeyRing.java
+++ b/router/java/src/net/i2p/router/PersistentKeyRing.java
@@ -38,11 +38,12 @@ public class PersistentKeyRing extends KeyRing {
@Override
public SessionKey remove(Object o) {
- if (o != null && o instanceof Hash) {
+ SessionKey rv = super.remove(o);
+ if (rv != null && o != null && o instanceof Hash) {
Hash h = (Hash) o;
_ctx.router().saveConfig(PROP_PFX + h.toBase64().replace("=", "$"), null);
}
- return super.remove(o);
+ return rv;
}
private void addFromProperties() {
diff --git a/router/java/src/net/i2p/router/RouterVersion.java b/router/java/src/net/i2p/router/RouterVersion.java
index f5c926454..43dca9240 100644
--- a/router/java/src/net/i2p/router/RouterVersion.java
+++ b/router/java/src/net/i2p/router/RouterVersion.java
@@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */
public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION;
- public final static long BUILD = 20;
+ public final static long BUILD = 21;
/** for example "-test" */
public final static String EXTRA = "";