Keyring: Separate local and remote dests on /configkeyring

Prohibit local changes on /configkeyring
Remove local keys from keyring on tunnel shutdown or encryption disable
Ensure subsession encryption setting matches primary session
(ticket #2108)
This commit is contained in:
zzz
2018-01-14 18:48:47 +00:00
parent 8ef042af6a
commit c2bfb80233
8 changed files with 69 additions and 12 deletions

View File

@ -33,6 +33,9 @@ public class ConfigKeyringHandler extends FormHandler {
} catch (DataFormatException dfe) {} } catch (DataFormatException dfe) {}
if (h == null || h.getData() == null) { if (h == null || h.getData() == null) {
addFormError(_t("Invalid destination")); 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) { } else if (sk.getData() == null) {
addFormError(_t("Invalid key")); addFormError(_t("Invalid key"));
} else { } else {
@ -42,12 +45,16 @@ public class ConfigKeyringHandler extends FormHandler {
} }
} else { // Delete } else { // Delete
if (h != null && h.getData() != null) { 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", addFormNotice(_t("Key for {0} removed from keyring",
Base32.encode(h.getData()) + ".b32.i2p")); Base32.encode(h.getData()) + ".b32.i2p"));
else } else {
addFormNotice(_t("Key for {0} not found in keyring", addFormNotice(_t("Key for {0} not found in keyring",
Base32.encode(h.getData()) + ".b32.i2p")); Base32.encode(h.getData()) + ".b32.i2p"));
}
} else { } else {
addFormError(_t("Invalid destination")); addFormError(_t("Invalid destination"));
} }

View File

@ -30,17 +30,31 @@ public class ConfigKeyringHelper extends HelperBase {
*/ */
private void renderStatusHTML(StringWriter out) throws IOException { private void renderStatusHTML(StringWriter out) throws IOException {
StringBuilder buf = new StringBuilder(1024); StringBuilder buf = new StringBuilder(1024);
buf.append("<h3>").append(_t("Local encrypted destinations")).append("</h3>");
render(buf, true);
buf.append("<h3>").append(_t("Remote encrypted destinations")).append("</h3>");
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<table class=\"configtable\"><tr><th align=\"left\">").append(_t("Destination")) buf.append("\n<table class=\"configtable\"><tr><th align=\"left\">").append(_t("Destination"))
.append("<th align=\"left\">").append(_t("Name")) .append("<th align=\"left\">").append(_t("Name"))
.append("<th align=\"left\">").append(_t("Encryption Key")) .append("<th align=\"left\">").append(_t("Encryption Key"))
.append("</tr>"); .append("</tr>");
for (Map.Entry<Hash, SessionKey> e : _context.keyRing().entrySet()) { for (Map.Entry<Hash, SessionKey> e : _context.keyRing().entrySet()) {
buf.append("\n<tr><td>");
Hash h = e.getKey(); Hash h = e.getKey();
if (local != _context.clientManager().isLocal(h))
continue;
buf.append("\n<tr><td>");
buf.append(Base32.encode(h.getData())).append(".b32.i2p"); buf.append(Base32.encode(h.getData())).append(".b32.i2p");
buf.append("</td><td>"); buf.append("</td><td>");
Destination dest = _context.netDb().lookupDestinationLocally(h); Destination dest = _context.netDb().lookupDestinationLocally(h);
if (dest != null && _context.clientManager().isLocal(dest)) { if (dest != null && local) {
TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(h); TunnelPoolSettings in = _context.tunnelManager().getInboundSettings(h);
if (in != null && in.getDestinationNickname() != null) if (in != null && in.getDestinationNickname() != null)
buf.append(in.getDestinationNickname()); buf.append(in.getDestinationNickname());
@ -55,7 +69,5 @@ public class ConfigKeyringHelper extends HelperBase {
buf.append("</td>\n"); buf.append("</td>\n");
} }
buf.append("</table>\n"); buf.append("</table>\n");
out.write(buf.toString());
out.flush();
} }
} }

View File

@ -360,6 +360,10 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
session.destroySession(); session.destroySession();
} catch (I2PSessionException ise) {} } 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() { private void closeSocket() {
if (_log.shouldLog(Log.INFO)) if (_log.shouldLog(Log.INFO))
_log.info(getPrefix() + "Closing the socket", new Exception("closeSocket")); _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) { synchronized(_stateLock) {
changeState(State.CLOSING); changeState(State.CLOSING);
locked_closeSocket(); locked_closeSocket();
@ -1217,6 +1225,9 @@ public abstract class I2PSessionImpl implements I2PSession, I2CPMessageReader.I2
} }
synchronized (_subsessionLock) { synchronized (_subsessionLock) {
for (SubSession sess : _subsessions) { for (SubSession sess : _subsessions) {
d = sess.getMyDestination();
if (d != null)
_context.keyRing().remove(d.calculateHash());
sess.changeState(State.CLOSED); sess.changeState(State.CLOSED);
sess.setSessionId(null); sess.setSessionId(null);
sess.setLeaseSet(null); sess.setLeaseSet(null);

View File

@ -12,6 +12,7 @@ package net.i2p.client.impl;
import java.io.EOFException; import java.io.EOFException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Map; import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import net.i2p.I2PAppContext; import net.i2p.I2PAppContext;
@ -21,6 +22,7 @@ import net.i2p.crypto.SigType;
import net.i2p.data.DataFormatException; import net.i2p.data.DataFormatException;
import net.i2p.data.DataHelper; import net.i2p.data.DataHelper;
import net.i2p.data.Destination; import net.i2p.data.Destination;
import net.i2p.data.Hash;
import net.i2p.data.Lease; import net.i2p.data.Lease;
import net.i2p.data.LeaseSet; import net.i2p.data.LeaseSet;
import net.i2p.data.PrivateKey; import net.i2p.data.PrivateKey;
@ -158,17 +160,27 @@ class RequestLeaseSetMessageHandler extends HandlerImpl {
leaseSet.setEncryptionKey(li.getPublicKey()); leaseSet.setEncryptionKey(li.getPublicKey());
leaseSet.setSigningKey(li.getSigningPublicKey()); leaseSet.setSigningKey(li.getSigningPublicKey());
boolean encrypt = Boolean.parseBoolean(session.getOptions().getProperty("i2cp.encryptLeaseSet")); // SubSession options aren't updated via the gui, so use the primary options
String sk = session.getOptions().getProperty("i2cp.leaseSetKey"); 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) { if (encrypt && sk != null) {
SessionKey key = new SessionKey(); SessionKey key = new SessionKey();
try { try {
key.fromBase64(sk); key.fromBase64(sk);
leaseSet.encrypt(key); leaseSet.encrypt(key);
_context.keyRing().put(session.getMyDestination().calculateHash(), key); _context.keyRing().put(h, key);
} catch (DataFormatException dfe) { } catch (DataFormatException dfe) {
_log.error("Bad leaseset key: " + sk); _log.error("Bad leaseset key: " + sk);
_context.keyRing().remove(h);
} }
} else {
_context.keyRing().remove(h);
} }
try { try {
leaseSet.sign(session.getPrivateKey()); leaseSet.sign(session.getPrivateKey());

View File

@ -85,6 +85,13 @@ class SubSession extends I2PSessionMuxedImpl {
@Override @Override
public void updateOptions(Properties options) {} 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 * Connect to the router and establish a session. This call blocks until
* a session is granted. * a session is granted.

View File

@ -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 2018-01-13 zzz
* SessionKeyManager: Clean up sessions with excess tagsets * SessionKeyManager: Clean up sessions with excess tagsets

View File

@ -38,11 +38,12 @@ public class PersistentKeyRing extends KeyRing {
@Override @Override
public SessionKey remove(Object o) { 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; Hash h = (Hash) o;
_ctx.router().saveConfig(PROP_PFX + h.toBase64().replace("=", "$"), null); _ctx.router().saveConfig(PROP_PFX + h.toBase64().replace("=", "$"), null);
} }
return super.remove(o); return rv;
} }
private void addFromProperties() { private void addFromProperties() {

View File

@ -18,7 +18,7 @@ public class RouterVersion {
/** deprecated */ /** deprecated */
public final static String ID = "Monotone"; public final static String ID = "Monotone";
public final static String VERSION = CoreVersion.VERSION; public final static String VERSION = CoreVersion.VERSION;
public final static long BUILD = 20; public final static long BUILD = 21;
/** for example "-test" */ /** for example "-test" */
public final static String EXTRA = ""; public final static String EXTRA = "";