Remove old v2 onion code from TorPlugin

This commit is contained in:
Torsten Grote
2021-11-17 10:25:43 -03:00
parent 5a8b822e08
commit 0dc2aba22f
3 changed files with 39 additions and 115 deletions

View File

@@ -1,13 +1,10 @@
package org.briarproject.bramble.api.plugin; package org.briarproject.bramble.api.plugin;
import static java.util.concurrent.TimeUnit.DAYS;
public interface TorConstants { public interface TorConstants {
TransportId ID = new TransportId("org.briarproject.bramble.tor"); TransportId ID = new TransportId("org.briarproject.bramble.tor");
// Transport properties // Transport properties
String PROP_ONION_V2 = "onion";
String PROP_ONION_V3 = "onion3"; String PROP_ONION_V3 = "onion3";
int DEFAULT_SOCKS_PORT = 59050; int DEFAULT_SOCKS_PORT = 59050;
@@ -21,14 +18,7 @@ public interface TorConstants {
String PREF_TOR_PORT = "port"; String PREF_TOR_PORT = "port";
String PREF_TOR_MOBILE = "useMobileData"; String PREF_TOR_MOBILE = "useMobileData";
String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging"; String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging";
String HS_PRIVATE_KEY_V2 = "onionPrivKey";
String HS_PRIVATE_KEY_V3 = "onionPrivKey3"; String HS_PRIVATE_KEY_V3 = "onionPrivKey3";
String HS_V3_CREATED = "onionPrivKey3Created";
/**
* How long to publish a v3 hidden service before retiring the v2 service.
*/
long V3_MIGRATION_PERIOD_MS = DAYS.toMillis(180);
// Values for PREF_TOR_NETWORK // Values for PREF_TOR_NETWORK
int PREF_TOR_NETWORK_AUTOMATIC = 0; int PREF_TOR_NETWORK_AUTOMATIC = 0;

View File

@@ -79,9 +79,7 @@ import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_PLUG
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING; import static org.briarproject.bramble.api.plugin.TorConstants.DEFAULT_PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_PRIVATE_KEY_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_PRIVATE_KEY_V3; import static org.briarproject.bramble.api.plugin.TorConstants.HS_PRIVATE_KEY_V3;
import static org.briarproject.bramble.api.plugin.TorConstants.HS_V3_CREATED;
import static org.briarproject.bramble.api.plugin.TorConstants.ID; import static org.briarproject.bramble.api.plugin.TorConstants.ID;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
@@ -90,12 +88,10 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3; import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_BATTERY;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_COUNTRY_BLOCKED;
import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA; import static org.briarproject.bramble.api.plugin.TorConstants.REASON_MOBILE_DATA;
import static org.briarproject.bramble.api.plugin.TorConstants.V3_MIGRATION_PERIOD_MS;
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES; import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.bramble.util.IoUtils.tryToClose; import static org.briarproject.bramble.util.IoUtils.tryToClose;
@@ -115,7 +111,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private static final String OWNER = "__OwningControllerProcess"; private static final String OWNER = "__OwningControllerProcess";
private static final int COOKIE_TIMEOUT_MS = 3000; private static final int COOKIE_TIMEOUT_MS = 3000;
private static final int COOKIE_POLLING_INTERVAL_MS = 200; private static final int COOKIE_POLLING_INTERVAL_MS = 200;
private static final Pattern ONION_V2 = Pattern.compile("[a-z2-7]{16}");
private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}"); private static final Pattern ONION_V3 = Pattern.compile("[a-z2-7]{56}");
private final Executor ioExecutor, wakefulIoExecutor; private final Executor ioExecutor, wakefulIoExecutor;
@@ -135,8 +130,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final int maxIdleTime; private final int maxIdleTime;
private final int socketTimeout; private final int socketTimeout;
private final File torDirectory, geoIpFile, configFile; private final File torDirectory, geoIpFile, configFile;
private int torSocksPort; private final int torSocksPort;
private int torControlPort; private final int torControlPort;
private final File doneFile, cookieFile; private final File doneFile, cookieFile;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@@ -284,6 +279,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (LOG.isLoggable(INFO)) listFiles(torDirectory); if (LOG.isLoggable(INFO)) listFiles(torDirectory);
throw new PluginException(); throw new PluginException();
} }
//noinspection BusyWait
Thread.sleep(COOKIE_POLLING_INTERVAL_MS); Thread.sleep(COOKIE_POLLING_INTERVAL_MS);
} }
LOG.info("Auth cookie created"); LOG.info("Auth cookie created");
@@ -412,6 +408,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
append(strb, "RunAsDaemon", 1); append(strb, "RunAsDaemon", 1);
append(strb, "SafeSocks", 1); append(strb, "SafeSocks", 1);
append(strb, "SocksPort", torSocksPort); append(strb, "SocksPort", torSocksPort);
//noinspection CharsetObjectCanBeUsed
return new ByteArrayInputStream( return new ByteArrayInputStream(
strb.toString().getBytes(Charset.forName("UTF-8"))); strb.toString().getBytes(Charset.forName("UTF-8")));
} }
@@ -478,54 +475,10 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private void publishHiddenService(String port) { private void publishHiddenService(String port) {
if (!state.isTorRunning()) return; if (!state.isTorRunning()) return;
// TODO: Remove support for v2 hidden services after a reasonable
// migration period (migration started 2020-06-30)
String privKey2 = settings.get(HS_PRIVATE_KEY_V2);
String privKey3 = settings.get(HS_PRIVATE_KEY_V3); String privKey3 = settings.get(HS_PRIVATE_KEY_V3);
String v3Created = settings.get(HS_V3_CREATED);
// Publish a v2 hidden service if we've already created one, and
// either we've never published a v3 hidden service or we're still
// in the migration period since first publishing it
if (!isNullOrEmpty(privKey2)) {
long now = clock.currentTimeMillis();
long then = v3Created == null ? now : Long.parseLong(v3Created);
if (now - then >= V3_MIGRATION_PERIOD_MS) retireV2HiddenService();
else publishV2HiddenService(port, privKey2);
}
publishV3HiddenService(port, privKey3); publishV3HiddenService(port, privKey3);
} }
private void publishV2HiddenService(String port, String privKey) {
LOG.info("Creating v2 hidden service");
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
Map<String, String> response;
try {
response = controlConnection.addOnion(privKey, portLines);
} catch (IOException e) {
logException(LOG, WARNING, e);
return;
}
if (!response.containsKey(HS_ADDRESS)) {
LOG.warning("Tor did not return a hidden service address");
return;
}
String onion2 = response.get(HS_ADDRESS);
if (LOG.isLoggable(INFO)) {
LOG.info("V2 hidden service " + scrubOnion(onion2));
}
// The hostname has already been published and the private key stored
}
private void retireV2HiddenService() {
LOG.info("Retiring v2 hidden service");
TransportProperties p = new TransportProperties();
p.put(PROP_ONION_V2, "");
callback.mergeLocalProperties(p);
Settings s = new Settings();
s.put(HS_PRIVATE_KEY_V2, "");
callback.mergeSettings(s);
}
private void publishV3HiddenService(String port, @Nullable String privKey) { private void publishV3HiddenService(String port, @Nullable String privKey) {
LOG.info("Creating v3 hidden service"); LOG.info("Creating v3 hidden service");
Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port); Map<Integer, String> portLines = singletonMap(80, "127.0.0.1:" + port);
@@ -562,7 +515,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
// Save the hidden service's private key for next time // Save the hidden service's private key for next time
Settings s = new Settings(); Settings s = new Settings();
s.put(HS_PRIVATE_KEY_V3, response.get(HS_PRIVKEY)); s.put(HS_PRIVATE_KEY_V3, response.get(HS_PRIVKEY));
s.put(HS_V3_CREATED, String.valueOf(clock.currentTimeMillis()));
callback.mergeSettings(s); callback.mergeSettings(s);
} }
} }
@@ -669,49 +621,30 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public DuplexTransportConnection createConnection(TransportProperties p) { public DuplexTransportConnection createConnection(TransportProperties p) {
if (getState() != ACTIVE) return null; if (getState() != ACTIVE) return null;
// TODO: Remove support for v2 hidden services after a reasonable
// migration period (migration started 2020-06-30)
String bestOnion = null, version = null;
String onion2 = p.get(PROP_ONION_V2);
String onion3 = p.get(PROP_ONION_V3); String onion3 = p.get(PROP_ONION_V3);
if (!isNullOrEmpty(onion2)) { if (onion3 != null && !ONION_V3.matcher(onion3).matches()) {
if (ONION_V2.matcher(onion2).matches()) { // Don't scrub the address so we can find the problem
bestOnion = onion2; if (LOG.isLoggable(INFO)) {
version = "v2"; LOG.info("Invalid v3 hostname: " + onion3);
} else {
// Don't scrub the address so we can find the problem
if (LOG.isLoggable(INFO))
LOG.info("Invalid v2 hostname: " + onion2);
} }
onion3 = null;
} }
if (!isNullOrEmpty(onion3)) { if (onion3 == null) return null;
if (ONION_V3.matcher(onion3).matches()) {
bestOnion = onion3;
version = "v3";
} else {
// Don't scrub the address so we can find the problem
if (LOG.isLoggable(INFO))
LOG.info("Invalid v3 hostname: " + onion3);
}
}
if (bestOnion == null) return null;
Socket s = null; Socket s = null;
try { try {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Connecting to " + version + " " LOG.info("Connecting to v3 " + scrubOnion(onion3));
+ scrubOnion(bestOnion));
} }
s = torSocketFactory.createSocket(bestOnion + ".onion", 80); s = torSocketFactory.createSocket(onion3 + ".onion", 80);
s.setSoTimeout(socketTimeout); s.setSoTimeout(socketTimeout);
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Connected to " + version + " " LOG.info("Connected to v3 " + scrubOnion(onion3));
+ scrubOnion(bestOnion));
} }
return new TorTransportConnection(this, s); return new TorTransportConnection(this, s);
} catch (IOException e) { } catch (IOException e) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Could not connect to " + version + " " LOG.info("Could not connect to v3 "
+ scrubOnion(bestOnion) + ": " + e.toString()); + scrubOnion(onion3) + ": " + e.toString());
} }
tryToClose(s, LOG, WARNING); tryToClose(s, LOG, WARNING);
return null; return null;
@@ -1005,17 +938,17 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Nullable @Nullable
private ServerSocket serverSocket = null; private ServerSocket serverSocket = null;
synchronized void setStarted() { private synchronized void setStarted() {
started = true; started = true;
callback.pluginStateChanged(getState()); callback.pluginStateChanged(getState());
} }
synchronized boolean isTorRunning() { private synchronized boolean isTorRunning() {
return started && !stopped; return started && !stopped;
} }
@Nullable @Nullable
synchronized ServerSocket setStopped() { private synchronized ServerSocket setStopped() {
stopped = true; stopped = true;
ServerSocket ss = serverSocket; ServerSocket ss = serverSocket;
serverSocket = null; serverSocket = null;
@@ -1023,44 +956,44 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return ss; return ss;
} }
synchronized void setBootstrapped() { private synchronized void setBootstrapped() {
bootstrapped = true; bootstrapped = true;
callback.pluginStateChanged(getState()); callback.pluginStateChanged(getState());
} }
synchronized boolean getAndSetCircuitBuilt() { private synchronized boolean getAndSetCircuitBuilt() {
boolean firstCircuit = !circuitBuilt; boolean firstCircuit = !circuitBuilt;
circuitBuilt = true; circuitBuilt = true;
callback.pluginStateChanged(getState()); callback.pluginStateChanged(getState());
return firstCircuit; return firstCircuit;
} }
synchronized void enableNetwork(boolean enable) { private synchronized void enableNetwork(boolean enable) {
networkInitialised = true; networkInitialised = true;
networkEnabled = enable; networkEnabled = enable;
if (!enable) circuitBuilt = false; if (!enable) circuitBuilt = false;
callback.pluginStateChanged(getState()); callback.pluginStateChanged(getState());
} }
synchronized void setReasonsDisabled(int reasonsDisabled) { private synchronized void setReasonsDisabled(int reasonsDisabled) {
settingsChecked = true; settingsChecked = true;
this.reasonsDisabled = reasonsDisabled; this.reasonsDisabled = reasonsDisabled;
callback.pluginStateChanged(getState()); callback.pluginStateChanged(getState());
} }
// Doesn't affect getState() // Doesn't affect getState()
synchronized boolean setServerSocket(ServerSocket ss) { private synchronized boolean setServerSocket(ServerSocket ss) {
if (stopped || serverSocket != null) return false; if (stopped || serverSocket != null) return false;
serverSocket = ss; serverSocket = ss;
return true; return true;
} }
// Doesn't affect getState() // Doesn't affect getState()
synchronized void clearServerSocket(ServerSocket ss) { private synchronized void clearServerSocket(ServerSocket ss) {
if (serverSocket == ss) serverSocket = null; if (serverSocket == ss) serverSocket = null;
} }
synchronized State getState() { private synchronized State getState() {
if (!started || stopped || !settingsChecked) { if (!started || stopped || !settingsChecked) {
return STARTING_STOPPING; return STARTING_STOPPING;
} }
@@ -1070,7 +1003,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return bootstrapped && circuitBuilt ? ACTIVE : ENABLING; return bootstrapped && circuitBuilt ? ACTIVE : ENABLING;
} }
synchronized int getReasonsDisabled() { private synchronized int getReasonsDisabled() {
return getState() == DISABLED ? reasonsDisabled : 0; return getState() == DISABLED ? reasonsDisabled : 0;
} }
} }

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -81,6 +82,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
private final DatabaseComponent db; private final DatabaseComponent db;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final CryptoComponent crypto;
private final ContactManager contactManager; private final ContactManager contactManager;
private final TransportPropertyManager transportPropertyManager; private final TransportPropertyManager transportPropertyManager;
private final MessagingManager messagingManager; private final MessagingManager messagingManager;
@@ -100,7 +102,9 @@ public class TestDataCreatorImpl implements TestDataCreator {
GroupFactory groupFactory, GroupFactory groupFactory,
PrivateMessageFactory privateMessageFactory, PrivateMessageFactory privateMessageFactory,
BlogPostFactory blogPostFactory, DatabaseComponent db, BlogPostFactory blogPostFactory, DatabaseComponent db,
IdentityManager identityManager, ContactManager contactManager, IdentityManager identityManager,
CryptoComponent crypto,
ContactManager contactManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
MessagingManager messagingManager, BlogManager blogManager, MessagingManager messagingManager, BlogManager blogManager,
ForumManager forumManager, ForumManager forumManager,
@@ -114,6 +118,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
this.blogPostFactory = blogPostFactory; this.blogPostFactory = blogPostFactory;
this.db = db; this.db = db;
this.identityManager = identityManager; this.identityManager = identityManager;
this.crypto = crypto;
this.contactManager = contactManager; this.contactManager = contactManager;
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
this.messagingManager = messagingManager; this.messagingManager = messagingManager;
@@ -247,7 +252,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
// Tor // Tor
TransportProperties tor = new TransportProperties(); TransportProperties tor = new TransportProperties();
String torAddress = getRandomTorAddress(); String torAddress = getRandomTorAddress();
tor.put(TorConstants.PROP_ONION_V2, torAddress); tor.put(TorConstants.PROP_ONION_V3, torAddress);
props.put(TorConstants.ID, tor); props.put(TorConstants.ID, tor);
return props; return props;
@@ -292,13 +297,9 @@ public class TestDataCreatorImpl implements TestDataCreator {
} }
private String getRandomTorAddress() { private String getRandomTorAddress() {
StringBuilder sb = new StringBuilder(); byte[] pubkeyBytes =
// address crypto.generateSignatureKeyPair().getPublic().getEncoded();
for (int i = 0; i < 16; i++) { return crypto.encodeOnionAddress(pubkeyBytes);
if (random.nextBoolean()) sb.append(2 + random.nextInt(6));
else sb.append((char) (random.nextInt(26) + 'a'));
}
return sb.toString();
} }
private void addAvatar(Contact c) throws DbException { private void addAvatar(Contact c) throws DbException {
@@ -351,7 +352,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
private void createRandomPrivateMessage(ContactId contactId, private void createRandomPrivateMessage(ContactId contactId,
GroupId groupId, int num) throws DbException { GroupId groupId, int num) throws DbException {
long timestamp = clock.currentTimeMillis() - num * 60 * 1000; long timestamp = clock.currentTimeMillis() - (long) num * 60 * 1000;
String text = getRandomText(); String text = getRandomText();
boolean local = random.nextBoolean(); boolean local = random.nextBoolean();
boolean autoDelete = random.nextBoolean(); boolean autoDelete = random.nextBoolean();
@@ -400,7 +401,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
private void addBlogPost(ContactId contactId, LocalAuthor author, int num) private void addBlogPost(ContactId contactId, LocalAuthor author, int num)
throws DbException { throws DbException {
Blog blog = blogManager.getPersonalBlog(author); Blog blog = blogManager.getPersonalBlog(author);
long timestamp = clock.currentTimeMillis() - num * 60 * 1000; long timestamp = clock.currentTimeMillis() - (long) num * 60 * 1000;
String text = getRandomText(); String text = getRandomText();
try { try {
BlogPost blogPost = blogPostFactory.createBlogPost(blog.getId(), BlogPost blogPost = blogPostFactory.createBlogPost(blog.getId(),
@@ -438,7 +439,7 @@ public class TestDataCreatorImpl implements TestDataCreator {
for (int i = 0; i < numForumPosts; i++) { for (int i = 0; i < numForumPosts; i++) {
Contact contact = contacts.get(random.nextInt(contacts.size())); Contact contact = contacts.get(random.nextInt(contacts.size()));
LocalAuthor author = localAuthors.get(contact); LocalAuthor author = localAuthors.get(contact);
long timestamp = clock.currentTimeMillis() - i * 60 * 1000; long timestamp = clock.currentTimeMillis() - (long) i * 60 * 1000;
String text = getRandomText(); String text = getRandomText();
MessageId parent = null; MessageId parent = null;
if (random.nextBoolean() && posts.size() > 0) { if (random.nextBoolean() && posts.size() > 0) {