mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 12:49:55 +01:00
Validate onion hostnames, only try to launch Tor on ARM, added comments.
This commit is contained in:
@@ -23,6 +23,7 @@ import java.util.Scanner;
|
|||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
import java.util.zip.ZipInputStream;
|
import java.util.zip.ZipInputStream;
|
||||||
|
|
||||||
import net.freehaven.tor.control.EventHandler;
|
import net.freehaven.tor.control.EventHandler;
|
||||||
@@ -54,6 +55,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
private static final int SOCKS_PORT = 59050, CONTROL_PORT = 59051;
|
private static final int SOCKS_PORT = 59050, CONTROL_PORT = 59051;
|
||||||
private static final int COOKIE_TIMEOUT = 3000; // Milliseconds
|
private static final int COOKIE_TIMEOUT = 3000; // Milliseconds
|
||||||
private static final int HOSTNAME_TIMEOUT = 30 * 1000; // Milliseconds
|
private static final int HOSTNAME_TIMEOUT = 30 * 1000; // Milliseconds
|
||||||
|
private static final Pattern ONION = Pattern.compile("[a-z2-7]{16}.onion");
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(TorPlugin.class.getName());
|
Logger.getLogger(TorPlugin.class.getName());
|
||||||
|
|
||||||
@@ -63,7 +65,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
private final long maxLatency, pollingInterval;
|
private final long maxLatency, pollingInterval;
|
||||||
|
|
||||||
private volatile boolean running = false;
|
private volatile boolean running = false;
|
||||||
private volatile Process torProcess = null;
|
private volatile Process tor = null;
|
||||||
private volatile ServerSocket socket = null;
|
private volatile ServerSocket socket = null;
|
||||||
|
|
||||||
TorPlugin(Executor pluginExecutor, Context appContext,
|
TorPlugin(Executor pluginExecutor, Context appContext,
|
||||||
@@ -89,41 +91,59 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean start() throws IOException {
|
public boolean start() throws IOException {
|
||||||
|
// Check that we have a Tor binary for this architecture
|
||||||
|
if(!Build.CPU_ABI.startsWith("armeabi")) {
|
||||||
|
if(LOG.isLoggable(INFO))
|
||||||
|
LOG.info("No Tor binary for this architecture");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Try to connect to an existing Tor process, if any
|
||||||
Socket s;
|
Socket s;
|
||||||
try {
|
try {
|
||||||
s = new Socket("127.0.0.1", CONTROL_PORT);
|
s = new Socket("127.0.0.1", CONTROL_PORT); // FIXME: Never closed
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Tor is already running");
|
if(LOG.isLoggable(INFO)) LOG.info("Tor is already running");
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
|
// Install the binary, GeoIP database and config file if necessary
|
||||||
if(!isInstalled() && !install()) {
|
if(!isInstalled() && !install()) {
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Could not install Tor");
|
if(LOG.isLoggable(INFO)) LOG.info("Could not install Tor");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Starting Tor");
|
if(LOG.isLoggable(INFO)) LOG.info("Starting Tor");
|
||||||
|
// Watch for the auth cookie file being created/updated
|
||||||
File cookieFile = getCookieFile();
|
File cookieFile = getCookieFile();
|
||||||
cookieFile.getParentFile().mkdirs();
|
cookieFile.getParentFile().mkdirs();
|
||||||
cookieFile.createNewFile();
|
cookieFile.createNewFile();
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
FileObserver obs = new WriteObserver(cookieFile, latch);
|
FileObserver obs = new WriteObserver(cookieFile, latch);
|
||||||
obs.startWatching();
|
obs.startWatching();
|
||||||
|
// Start a new Tor process
|
||||||
String torPath = getTorFile().getAbsolutePath();
|
String torPath = getTorFile().getAbsolutePath();
|
||||||
String configPath = getConfigFile().getAbsolutePath();
|
String configPath = getConfigFile().getAbsolutePath();
|
||||||
String[] command = { torPath, "-f", configPath };
|
String[] command = { torPath, "-f", configPath };
|
||||||
String home = "HOME=" + getTorDirectory().getAbsolutePath();
|
String home = "HOME=" + getTorDirectory().getAbsolutePath();
|
||||||
String[] environment = { home };
|
String[] environment = { home };
|
||||||
File dir = getTorDirectory();
|
File dir = getTorDirectory();
|
||||||
torProcess = Runtime.getRuntime().exec(command, environment, dir);
|
try {
|
||||||
|
tor = Runtime.getRuntime().exec(command, environment, dir);
|
||||||
|
} catch(SecurityException e1) {
|
||||||
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e1.toString(), e1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Log the process's standard output until it detaches
|
||||||
if(LOG.isLoggable(INFO)) {
|
if(LOG.isLoggable(INFO)) {
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
Scanner stdout = new Scanner(tor.getInputStream());
|
||||||
while(stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
while(stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
||||||
stdout.close();
|
stdout.close();
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
int exit = torProcess.waitFor();
|
// Wait for the process to detach successfully
|
||||||
|
int exit = tor.waitFor();
|
||||||
if(exit != 0) {
|
if(exit != 0) {
|
||||||
if(LOG.isLoggable(WARNING))
|
if(LOG.isLoggable(WARNING))
|
||||||
LOG.warning("Tor exited with value " + exit);
|
LOG.warning("Tor exited with value " + exit);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Wait for the auth cookie file to be created/updated
|
||||||
if(!latch.await(COOKIE_TIMEOUT, MILLISECONDS)) {
|
if(!latch.await(COOKIE_TIMEOUT, MILLISECONDS)) {
|
||||||
if(LOG.isLoggable(WARNING))
|
if(LOG.isLoggable(WARNING))
|
||||||
LOG.warning("Auth cookie not created");
|
LOG.warning("Auth cookie not created");
|
||||||
@@ -135,14 +155,17 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
LOG.warning("Interrupted while starting Tor");
|
LOG.warning("Interrupted while starting Tor");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
s = new Socket("127.0.0.1", CONTROL_PORT);
|
// Now we should be able to connect to the new process
|
||||||
|
s = new Socket("127.0.0.1", CONTROL_PORT); // FIXME: Never closed
|
||||||
}
|
}
|
||||||
|
// Open a control connection and authenticate using the cookie file
|
||||||
TorControlConnection control = new TorControlConnection(s);
|
TorControlConnection control = new TorControlConnection(s);
|
||||||
control.launchThread(true);
|
control.launchThread(true);
|
||||||
control.authenticate(read(getCookieFile()));
|
control.authenticate(read(getCookieFile()));
|
||||||
control.setEventHandler(this);
|
control.setEventHandler(this);
|
||||||
control.setEvents(Arrays.asList("NOTICE", "WARN", "ERR"));
|
control.setEvents(Arrays.asList("NOTICE", "WARN", "ERR"));
|
||||||
running = true;
|
running = true;
|
||||||
|
// Bind a server socket to receive incoming hidden service connections
|
||||||
pluginExecutor.execute(new Runnable() {
|
pluginExecutor.execute(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
bind();
|
bind();
|
||||||
@@ -159,20 +182,25 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
try {
|
try {
|
||||||
|
// Unzip the Tor binary to the filesystem
|
||||||
in = getTorInputStream();
|
in = getTorInputStream();
|
||||||
out = new FileOutputStream(getTorFile());
|
out = new FileOutputStream(getTorFile());
|
||||||
copy(in, out);
|
copy(in, out);
|
||||||
|
// Unzip the GeoIP database to the filesystem
|
||||||
in = getGeoIpInputStream();
|
in = getGeoIpInputStream();
|
||||||
out = new FileOutputStream(getGeoIpFile());
|
out = new FileOutputStream(getGeoIpFile());
|
||||||
copy(in, out);
|
copy(in, out);
|
||||||
|
// Copy the config file to the filesystem
|
||||||
in = getConfigInputStream();
|
in = getConfigInputStream();
|
||||||
out = new FileOutputStream(getConfigFile());
|
out = new FileOutputStream(getConfigFile());
|
||||||
copy(in, out);
|
copy(in, out);
|
||||||
|
// Make the Tor binary executable
|
||||||
if(!setExecutable(getTorFile())) {
|
if(!setExecutable(getTorFile())) {
|
||||||
if(LOG.isLoggable(WARNING))
|
if(LOG.isLoggable(WARNING))
|
||||||
LOG.warning("Could not make Tor executable");
|
LOG.warning("Could not make Tor executable");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Create a file to indicate that installation succeeded
|
||||||
File done = getDoneFile();
|
File done = getDoneFile();
|
||||||
done.createNewFile();
|
done.createNewFile();
|
||||||
return true;
|
return true;
|
||||||
@@ -227,6 +255,8 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
if(LOG.isLoggable(WARNING))
|
if(LOG.isLoggable(WARNING))
|
||||||
LOG.warning("Interrupted while executing chmod");
|
LOG.warning("Interrupted while executing chmod");
|
||||||
Thread.currentThread().interrupt();
|
Thread.currentThread().interrupt();
|
||||||
|
} catch(SecurityException e) {
|
||||||
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -265,10 +295,12 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void bind() {
|
private void bind() {
|
||||||
|
// If there's already a port number stored in config, reuse it
|
||||||
String portString = callback.getConfig().get("port");
|
String portString = callback.getConfig().get("port");
|
||||||
int port;
|
int port;
|
||||||
if(StringUtils.isNullOrEmpty(portString)) port = 0;
|
if(StringUtils.isNullOrEmpty(portString)) port = 0;
|
||||||
else port = Integer.parseInt(portString);
|
else port = Integer.parseInt(portString);
|
||||||
|
// Bind a server socket to receive connections from the Tor process
|
||||||
ServerSocket ss = null;
|
ServerSocket ss = null;
|
||||||
try {
|
try {
|
||||||
ss = new ServerSocket();
|
ss = new ServerSocket();
|
||||||
@@ -282,15 +314,18 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
socket = ss;
|
socket = ss;
|
||||||
|
// Store the port number
|
||||||
final String localPort = String.valueOf(ss.getLocalPort());
|
final String localPort = String.valueOf(ss.getLocalPort());
|
||||||
TransportConfig c = new TransportConfig();
|
TransportConfig c = new TransportConfig();
|
||||||
c.put("port", localPort);
|
c.put("port", localPort);
|
||||||
callback.mergeConfig(c);
|
callback.mergeConfig(c);
|
||||||
|
// Create a hidden service if necessary
|
||||||
pluginExecutor.execute(new Runnable() {
|
pluginExecutor.execute(new Runnable() {
|
||||||
public void run() {
|
public void run() {
|
||||||
publishHiddenService(localPort);
|
publishHiddenService(localPort);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
// Accept incoming hidden service connections from the Tor process
|
||||||
acceptContactConnections(ss);
|
acceptContactConnections(ss);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -308,11 +343,13 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
if(!hostnameFile.exists()) {
|
if(!hostnameFile.exists()) {
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Creating hidden service");
|
if(LOG.isLoggable(INFO)) LOG.info("Creating hidden service");
|
||||||
try {
|
try {
|
||||||
|
// Watch for the hostname file being created/updated
|
||||||
hostnameFile.getParentFile().mkdirs();
|
hostnameFile.getParentFile().mkdirs();
|
||||||
hostnameFile.createNewFile();
|
hostnameFile.createNewFile();
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
FileObserver obs = new WriteObserver(hostnameFile, latch);
|
FileObserver obs = new WriteObserver(hostnameFile, latch);
|
||||||
obs.startWatching();
|
obs.startWatching();
|
||||||
|
// Open a control connection and update the Tor config
|
||||||
String dir = getTorDirectory().getAbsolutePath();
|
String dir = getTorDirectory().getAbsolutePath();
|
||||||
List<String> config = Arrays.asList("HiddenServiceDir " + dir,
|
List<String> config = Arrays.asList("HiddenServiceDir " + dir,
|
||||||
"HiddenServicePort 80 127.0.0.1:" + port);
|
"HiddenServicePort 80 127.0.0.1:" + port);
|
||||||
@@ -323,6 +360,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
control.authenticate(read(getCookieFile()));
|
control.authenticate(read(getCookieFile()));
|
||||||
control.setConf(config);
|
control.setConf(config);
|
||||||
control.saveConf();
|
control.saveConf();
|
||||||
|
// Wait for the hostname file to be created/updated
|
||||||
if(!latch.await(HOSTNAME_TIMEOUT, MILLISECONDS)) {
|
if(!latch.await(HOSTNAME_TIMEOUT, MILLISECONDS)) {
|
||||||
if(LOG.isLoggable(WARNING))
|
if(LOG.isLoggable(WARNING))
|
||||||
LOG.warning("Hidden service not created");
|
LOG.warning("Hidden service not created");
|
||||||
@@ -337,6 +375,7 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
LOG.warning("Interrupted while creating hidden service");
|
LOG.warning("Interrupted while creating hidden service");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Publish the hidden service's onion hostname in transport properties
|
||||||
try {
|
try {
|
||||||
String hostname = new String(read(hostnameFile), "UTF-8").trim();
|
String hostname = new String(read(hostnameFile), "UTF-8").trim();
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Hidden service " + hostname);
|
if(LOG.isLoggable(INFO)) LOG.info("Hidden service " + hostname);
|
||||||
@@ -379,9 +418,9 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
control.shutdownTor("TERM");
|
control.shutdownTor("TERM");
|
||||||
} catch(IOException e) {
|
} catch(IOException e) {
|
||||||
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
if(torProcess != null) {
|
if(tor != null) {
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Killing Tor");
|
if(LOG.isLoggable(INFO)) LOG.info("Killing Tor");
|
||||||
torProcess.destroy();
|
tor.destroy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -419,7 +458,10 @@ class TorPlugin implements DuplexPlugin, EventHandler {
|
|||||||
if(p == null) return null;
|
if(p == null) return null;
|
||||||
String onion = p.get("onion");
|
String onion = p.get("onion");
|
||||||
if(StringUtils.isNullOrEmpty(onion)) return null;
|
if(StringUtils.isNullOrEmpty(onion)) return null;
|
||||||
// FIXME: Check that it's an onion hostname
|
if(!ONION.matcher(onion).matches()) {
|
||||||
|
if(LOG.isLoggable(INFO)) LOG.info("Invalid hostname: " + onion);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if(LOG.isLoggable(INFO)) LOG.info("Connecting to " + onion);
|
if(LOG.isLoggable(INFO)) LOG.info("Connecting to " + onion);
|
||||||
Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", SOCKS_PORT);
|
Socks5Proxy proxy = new Socks5Proxy("127.0.0.1", SOCKS_PORT);
|
||||||
|
|||||||
Reference in New Issue
Block a user