mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-16 20:59:54 +01:00
Merge branch '578-tor-ports' into 'master'
Don't connect to Tor if it's already running For some time now we've had a reliable way of shutting down the Tor process (the __OwningControllerProcess command line argument combined with the TAKEOWNERSHIP command), but TorPlugin#start() still assumes that Tor may already be running. This allows another app to bind the Tor control and SOCKS ports and collect confidential data from Briar (#578). It also allows two Briar instances running on the same device to try to communicate with the same Tor process, which prevents proper shutdown (#572). This patch prevents the Tor plugin from starting unless it's able to start its own Tor process with the expected control and SOCKS ports. If two Briar instances are running on the same device, only one of them will be able to use Tor. The other should fail to start its Tor plugin and then function normally without Tor access, including normal shutdown. Fixes #572, #578. Open another ticket if you want two Briar instances on the same device to have their own Tor processes. :-) See merge request !272
This commit is contained in:
@@ -150,74 +150,64 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public boolean start() throws IOException {
|
public boolean start() throws IOException {
|
||||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
if (used.getAndSet(true)) throw new IllegalStateException();
|
||||||
// Try to connect to an existing Tor process if there is one
|
// Install the binary, possibly overwriting an older version
|
||||||
boolean startProcess = false;
|
if (!installBinary()) {
|
||||||
|
LOG.warning("Could not install Tor binary");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Install the GeoIP database and config file if necessary
|
||||||
|
if (!isConfigInstalled() && !installConfig()) {
|
||||||
|
LOG.warning("Could not install Tor config");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
LOG.info("Starting Tor");
|
||||||
|
// Watch for the auth cookie file being updated
|
||||||
|
cookieFile.getParentFile().mkdirs();
|
||||||
|
cookieFile.createNewFile();
|
||||||
|
CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
FileObserver obs = new WriteObserver(cookieFile, latch);
|
||||||
|
obs.startWatching();
|
||||||
|
// Start a new Tor process
|
||||||
|
String torPath = torFile.getAbsolutePath();
|
||||||
|
String configPath = configFile.getAbsolutePath();
|
||||||
|
String pid = String.valueOf(android.os.Process.myPid());
|
||||||
|
String[] cmd = {torPath, "-f", configPath, OWNER, pid};
|
||||||
|
String[] env = {"HOME=" + torDirectory.getAbsolutePath()};
|
||||||
|
Process torProcess;
|
||||||
try {
|
try {
|
||||||
controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
|
torProcess = Runtime.getRuntime().exec(cmd, env, torDirectory);
|
||||||
LOG.info("Tor is already running");
|
} catch (SecurityException e1) {
|
||||||
} catch (IOException e) {
|
if (LOG.isLoggable(WARNING))
|
||||||
LOG.info("Tor is not running");
|
LOG.log(WARNING, e1.toString(), e1);
|
||||||
startProcess = true;
|
return false;
|
||||||
// Install the binary, possibly overwriting an older version
|
}
|
||||||
if (!installBinary()) {
|
// Log the process's standard output until it detaches
|
||||||
LOG.warning("Could not install Tor binary");
|
if (LOG.isLoggable(INFO)) {
|
||||||
return false;
|
Scanner stdout = new Scanner(torProcess.getInputStream());
|
||||||
}
|
while (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
||||||
// Install the GeoIP database and config file if necessary
|
stdout.close();
|
||||||
if (!isConfigInstalled() && !installConfig()) {
|
}
|
||||||
LOG.warning("Could not install Tor config");
|
try {
|
||||||
return false;
|
// Wait for the process to detach or exit
|
||||||
}
|
int exit = torProcess.waitFor();
|
||||||
LOG.info("Starting Tor");
|
if (exit != 0) {
|
||||||
// Watch for the auth cookie file being created/updated
|
|
||||||
cookieFile.getParentFile().mkdirs();
|
|
||||||
cookieFile.createNewFile();
|
|
||||||
CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
FileObserver obs = new WriteObserver(cookieFile, latch);
|
|
||||||
obs.startWatching();
|
|
||||||
// Start a new Tor process
|
|
||||||
String torPath = torFile.getAbsolutePath();
|
|
||||||
String configPath = configFile.getAbsolutePath();
|
|
||||||
String pid = String.valueOf(android.os.Process.myPid());
|
|
||||||
String[] cmd = {torPath, "-f", configPath, OWNER, pid};
|
|
||||||
String[] env = {"HOME=" + torDirectory.getAbsolutePath()};
|
|
||||||
Process torProcess;
|
|
||||||
try {
|
|
||||||
torProcess = Runtime.getRuntime().exec(cmd, env, torDirectory);
|
|
||||||
} catch (SecurityException e1) {
|
|
||||||
if (LOG.isLoggable(WARNING))
|
if (LOG.isLoggable(WARNING))
|
||||||
LOG.log(WARNING, e1.toString(), e1);
|
LOG.warning("Tor exited with value " + exit);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Log the process's standard output until it detaches
|
// Wait for the auth cookie file to be created/updated
|
||||||
if (LOG.isLoggable(INFO)) {
|
if (!latch.await(COOKIE_TIMEOUT, MILLISECONDS)) {
|
||||||
Scanner stdout = new Scanner(torProcess.getInputStream());
|
LOG.warning("Auth cookie not created");
|
||||||
while (stdout.hasNextLine()) LOG.info(stdout.nextLine());
|
if (LOG.isLoggable(INFO)) listFiles(torDirectory);
|
||||||
stdout.close();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Wait for the process to detach or exit
|
|
||||||
int exit = torProcess.waitFor();
|
|
||||||
if (exit != 0) {
|
|
||||||
if (LOG.isLoggable(WARNING))
|
|
||||||
LOG.warning("Tor exited with value " + exit);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Wait for the auth cookie file to be created/updated
|
|
||||||
if (!latch.await(COOKIE_TIMEOUT, MILLISECONDS)) {
|
|
||||||
LOG.warning("Auth cookie not created");
|
|
||||||
if (LOG.isLoggable(INFO)) listFiles(torDirectory);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
} catch (InterruptedException e1) {
|
|
||||||
LOG.warning("Interrupted while starting Tor");
|
|
||||||
Thread.currentThread().interrupt();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Now we should be able to connect to the new process
|
} catch (InterruptedException e1) {
|
||||||
controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
|
LOG.warning("Interrupted while starting Tor");
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// Open a control connection and authenticate using the cookie file
|
// Open a control connection and authenticate using the cookie file
|
||||||
|
controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
|
||||||
controlConnection = new TorControlConnection(controlSocket);
|
controlConnection = new TorControlConnection(controlSocket);
|
||||||
controlConnection.authenticate(read(cookieFile));
|
controlConnection.authenticate(read(cookieFile));
|
||||||
// Tell Tor to exit when the control connection is closed
|
// Tell Tor to exit when the control connection is closed
|
||||||
@@ -227,13 +217,11 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
// Register to receive events from the Tor process
|
// Register to receive events from the Tor process
|
||||||
controlConnection.setEventHandler(this);
|
controlConnection.setEventHandler(this);
|
||||||
controlConnection.setEvents(Arrays.asList(EVENTS));
|
controlConnection.setEvents(Arrays.asList(EVENTS));
|
||||||
// If Tor was already running, find out whether it's bootstrapped
|
// Check whether Tor has already bootstrapped
|
||||||
if (!startProcess) {
|
String phase = controlConnection.getInfo("status/bootstrap-phase");
|
||||||
String phase = controlConnection.getInfo("status/bootstrap-phase");
|
if (phase != null && phase.contains("PROGRESS=100")) {
|
||||||
if (phase != null && phase.contains("PROGRESS=100")) {
|
LOG.info("Tor has already bootstrapped");
|
||||||
LOG.info("Tor has already bootstrapped");
|
connectionStatus.setBootstrapped();
|
||||||
connectionStatus.setBootstrapped();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Register to receive network status events
|
// Register to receive network status events
|
||||||
networkStateReceiver = new NetworkStateReceiver();
|
networkStateReceiver = new NetworkStateReceiver();
|
||||||
@@ -271,6 +259,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean installConfig() {
|
private boolean installConfig() {
|
||||||
|
LOG.info("Installing Tor config");
|
||||||
InputStream in = null;
|
InputStream in = null;
|
||||||
OutputStream out = null;
|
OutputStream out = null;
|
||||||
try {
|
try {
|
||||||
@@ -500,19 +489,15 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
tryToClose(socket);
|
tryToClose(socket);
|
||||||
if (networkStateReceiver != null)
|
if (networkStateReceiver != null)
|
||||||
appContext.unregisterReceiver(networkStateReceiver);
|
appContext.unregisterReceiver(networkStateReceiver);
|
||||||
try {
|
if (controlSocket != null && controlConnection != null) {
|
||||||
LOG.info("Stopping Tor");
|
try {
|
||||||
if (controlSocket == null)
|
LOG.info("Stopping Tor");
|
||||||
controlSocket = new Socket("127.0.0.1", CONTROL_PORT);
|
controlConnection.setConf("DisableNetwork", "1");
|
||||||
if (controlConnection == null) {
|
controlConnection.shutdownTor("TERM");
|
||||||
controlConnection = new TorControlConnection(controlSocket);
|
controlSocket.close();
|
||||||
controlConnection.authenticate(read(cookieFile));
|
} catch (IOException e) {
|
||||||
|
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||||
}
|
}
|
||||||
controlConnection.setConf("DisableNetwork", "1");
|
|
||||||
controlConnection.shutdownTor("TERM");
|
|
||||||
controlSocket.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
}
|
||||||
wakeLock.release();
|
wakeLock.release();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user