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"); for (Map.Entry e : _context.keyRing().entrySet()) { - buf.append("\n\n"); } buf.append("
").append(_t("Destination")) .append("").append(_t("Name")) .append("").append(_t("Encryption Key")) .append("
"); 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"); - 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 = "";