diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 190d959e..e8e430be 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,11 +1,11 @@ + package="org.torproject.android" android:versionName="0.2.2.14-orbot-alpha-1.0.2" android:versionCode="8"> + - + + + + + + + + + + - + diff --git a/assets/iptables b/assets/iptables deleted file mode 100644 index 50776a83..00000000 Binary files a/assets/iptables and /dev/null differ diff --git a/assets/torrc b/assets/torrc index 3de18724..1c687a09 100644 --- a/assets/torrc +++ b/assets/torrc @@ -1,9 +1,8 @@ SocksPort 9050 SocksListenAddress 127.0.0.1 SafeSocks 1 -DNSPort 5400 Log notice stdout -Log debug syslog +##Log debug syslog DataDirectory /data/data/org.torproject.android/data ControlPort 9051 CookieAuthentication 1 @@ -12,3 +11,4 @@ RelayBandwidthBurst 20 KBytes UseBridges 0 AutomapHostsOnResolve 1 TransPort 9040 +DNSPort 5400 diff --git a/default.properties b/default.properties index d8796c79..06c219ae 100644 --- a/default.properties +++ b/default.properties @@ -10,5 +10,5 @@ # Indicates whether an apk should be generated for each density. split.density=false # Project target. -target=Google Inc.:Google APIs:3 +target=android-3 apk-configurations= diff --git a/res/values/strings.xml b/res/values/strings.xml index f89abf6a..67d66e35 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1,5 +1,6 @@ + Hidden Services Orbot 1.0.2 http://orbot/ @@ -72,6 +73,9 @@ and all DNS requests. This includes the built-in Browser, Gmail, YouTube and Map Back Finish +Okay +Cancel + Orbot brings Tor to Android. Tor is free software and an open network that helps you defend against a form of network surveillance that threatens personal freedom and privacy, confidential business activities and relationships, and state security known as traffic analysis.\n\n*WARNING:* Simply installing Orbot will _not_ magically anonymize your mobile traffic! This wizard will help you get started. @@ -115,4 +119,5 @@ and all DNS requests. This includes the built-in Browser, Gmail, YouTube and Map You\'ve successfully connected to the Tor network - but this does NOT mean your device is secure. You can use the \'Check\' option from the menu to test your browser. \n\nVisit us at https://guardianproject.info/apps/orbot or send an email to help@guardianproject.info to learn more. + This will open your default web browser to https://check.torproject.org in order to see if Orbot is probably configured and you are connected to Tor. diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index 573311fc..39b55dbf 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -27,16 +27,6 @@ android:enabled="true"/> - @@ -100,4 +90,9 @@ android:dialogTitle="Enter ports" /> + + + + + diff --git a/src/org/torproject/android/Orbot.java b/src/org/torproject/android/Orbot.java index 017054a9..304d40b4 100644 --- a/src/org/torproject/android/Orbot.java +++ b/src/org/torproject/android/Orbot.java @@ -3,10 +3,15 @@ package org.torproject.android; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.util.StringTokenizer; import org.torproject.android.service.ITorService; import org.torproject.android.service.ITorServiceCallback; +import org.torproject.android.service.TorServiceConstants; import org.torproject.android.service.TorTransProxy; import android.app.Activity; @@ -62,6 +67,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants /* Tor Service interaction */ /* The primary interface we will be calling on the service. */ ITorService mService = null; + private boolean autoStartOnBind = false; Orbot mOrbot = null; @@ -147,15 +153,11 @@ public class Orbot extends Activity implements OnClickListener, TorConstants } else if (item.getItemId() == 4) { - this.showSettings(); + showSettings(); } else if (item.getItemId() == 6) { - this.showMessageLog(); - } - else if (item.getItemId() == 2) - { - openBrowser(URL_TOR_CHECK); + showMessageLog(); } else if (item.getItemId() == 3) { @@ -163,8 +165,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants } else if (item.getItemId() == 7) { - //launch check.torproject.org - openBrowser(URL_TOR_CHECK); + doTorCheck(); } else if (item.getItemId() == 8) { @@ -180,15 +181,17 @@ public class Orbot extends Activity implements OnClickListener, TorConstants private void doExit () { try { + stopTor(); - NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancelAll(); unbindService(); stopService(new Intent(ITorService.class.getName())); - + + NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationManager.cancelAll(); + } catch (RemoteException e) { Log.w(TAG, e); @@ -247,6 +250,69 @@ public class Orbot extends Activity implements OnClickListener, TorConstants logBuffer.append(logText); } + private void doTorCheck () + { + + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which){ + case DialogInterface.BUTTON_POSITIVE: + + openBrowser(URL_TOR_CHECK); + + + + break; + + case DialogInterface.BUTTON_NEGATIVE: + + //do nothing + break; + } + } + }; + + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(R.string.tor_check).setPositiveButton(R.string.btn_okay, dialogClickListener) + .setNegativeButton(R.string.btn_cancel, dialogClickListener).show(); + + } + + private void enableHiddenServicePort (int hsPort) + { + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mOrbot); + Editor pEdit = prefs.edit(); + + String hsPortString = prefs.getString("pref_hs_ports", ""); + + if (hsPortString.length() > 0 && hsPortString.indexOf(hsPort+"")==-1) + hsPortString += ',' + hsPort; + else + hsPortString = hsPort + ""; + + pEdit.putString("pref_hs_ports", hsPortString); + pEdit.putBoolean("pref_hs_enable", true); + + pEdit.commit(); + + try { + processSettings(); + + String onionHostname = getHiddenServiceHostname(); + + Intent nResult = new Intent(); + nResult.putExtra("hs_host", onionHostname); + setResult(RESULT_OK, nResult); + + } catch (RemoteException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + + } + /* (non-Javadoc) * @see android.app.Activity#onResume() */ @@ -254,33 +320,80 @@ public class Orbot extends Activity implements OnClickListener, TorConstants super.onResume(); - NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - mNotificationManager.cancelAll(); - - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mOrbot); - boolean showWizard = prefs.getBoolean("show_wizard",true); + String action = getIntent().getAction(); - if (showWizard) + if (action != null) { - - Editor pEdit = prefs.edit(); + if (action.equals("org.torproject.android.REQUEST_HS_PORT")) + { + + DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + switch (which){ + case DialogInterface.BUTTON_POSITIVE: + + int hsPort = getIntent().getIntExtra("hs_port", -1); + + enableHiddenServicePort (hsPort); + + finish(); + + + break; + + case DialogInterface.BUTTON_NEGATIVE: + //No button clicked + finish(); + break; + } + } + }; + + int hsPort = getIntent().getIntExtra("hs_port", -1); + + String requestMsg = "An app wants to open a server port (" + hsPort + ") to the Tor network. This is safe if you trust the app."; + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setMessage(requestMsg).setPositiveButton("Allow", dialogClickListener) + .setNegativeButton("Deny", dialogClickListener).show(); + - pEdit.putBoolean("show_wizard",false); + } + else if (action.equals("org.torproject.android.START_TOR")) + { + autoStartOnBind = true; + + if (mService == null) + bindService(); + + } - pEdit.commit(); - - showHelp(); } else { + + NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + mNotificationManager.cancelAll(); + SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mOrbot); + + boolean showWizard = prefs.getBoolean("show_wizard",true); + if (showWizard) + { + + Editor pEdit = prefs.edit(); + + pEdit.putBoolean("show_wizard",false); + + pEdit.commit(); + + showHelp(); + } } - } /* (non-Javadoc) @@ -293,8 +406,7 @@ public class Orbot extends Activity implements OnClickListener, TorConstants startService(new Intent(INTENT_TOR_SERVICE)); bindService (); - //updateStatus (""); - + updateStatus (""); } @@ -414,8 +526,12 @@ public class Orbot extends Activity implements OnClickListener, TorConstants boolean ReachableAddresses = prefs.getBoolean(PREF_REACHABLE_ADDRESSES,false); + boolean enableHiddenServices = prefs.getBoolean("pref_hs_enable", false); + + boolean enableTransparentProxy = prefs.getBoolean(PREF_TRANSPARENT, false); + mService.updateTransProxy(); String bridgeList = prefs.getString(PREF_BRIDGES_LIST,""); @@ -469,6 +585,10 @@ public class Orbot extends Activity implements OnClickListener, TorConstants mService.updateConfiguration("ReachableAddresses", ReachableAddressesPorts, false); } + else + { + mService.updateConfiguration("ReachableAddresses", "", false); + } } catch (Exception e) { @@ -487,6 +607,12 @@ public class Orbot extends Activity implements OnClickListener, TorConstants mService.updateConfiguration("ExitPolicy", "reject *:*", false); } + else + { + mService.updateConfiguration("ORPort", "", false); + mService.updateConfiguration("Nickname", "", false); + mService.updateConfiguration("ExitPolicy", "", false); + } } catch (Exception e) { @@ -495,8 +621,48 @@ public class Orbot extends Activity implements OnClickListener, TorConstants return; } - if (mService != null) - mService.saveConfiguration(); + if (enableHiddenServices) + { + mService.updateConfiguration("HiddenServiceDir","/data/data/org.torproject.android/", false); + + String hsPorts = prefs.getString("pref_hs_ports",""); + + StringTokenizer st = new StringTokenizer (hsPorts,","); + String hsPortConfig = null; + + while (st.hasMoreTokens()) + { + hsPortConfig = st.nextToken(); + + if (hsPortConfig.indexOf(":")==-1) //setup the port to localhost if not specifed + { + hsPortConfig = hsPortConfig + " 127.0.0.1:" + hsPortConfig; + } + + mService.updateConfiguration("HiddenServicePort",hsPortConfig, false); + } + + //force save now so the hostname file gets generated + mService.saveConfiguration(); + + String onionHostname = getHiddenServiceHostname(); + + if (onionHostname != null) + { + + Editor pEdit = prefs.edit(); + pEdit.putString("pref_hs_hostname",onionHostname); + pEdit.commit(); + + } + } + else + { + mService.updateConfiguration("HiddenServiceDir","", false); + + } + + mService.saveConfiguration(); } catch (Exception e) { @@ -509,6 +675,20 @@ public class Orbot extends Activity implements OnClickListener, TorConstants } + private String getHiddenServiceHostname () + { + String appHome = "/data/data/" + TorServiceConstants.TOR_APP_USERNAME + "/"; + + File file = new File(appHome, "hostname"); + try { + String onionHostname = Utils.readString(new FileInputStream(file)); + return onionHostname.trim(); + } catch (FileNotFoundException e) { + Log.i(TAG, "unable to read onion hostname file",e); + return null; + } + } + private void showAlert(String title, String msg) { @@ -561,7 +741,21 @@ public class Orbot extends Activity implements OnClickListener, TorConstants } + boolean enableHiddenServices = prefs.getBoolean("pref_hs_enable", false); + if (enableHiddenServices) + { + String onionHostname = getHiddenServiceHostname(); + + if (onionHostname != null) + { + Editor pEdit = prefs.edit(); + pEdit.putString("pref_hs_hostname",onionHostname); + pEdit.commit(); + + } + + } } @@ -651,14 +845,18 @@ public class Orbot extends Activity implements OnClickListener, TorConstants else if (mService.getStatus() == STATUS_READY) { - startTor(); - + if (event.getAction() == MotionEvent.ACTION_UP) + { + startTor(); + } } else { - stopTor(); - + if (event.getAction() == MotionEvent.ACTION_DOWN) + { + stopTor(); + } } } @@ -799,18 +997,29 @@ public class Orbot extends Activity implements OnClickListener, TorConstants mService = ITorService.Stub.asInterface(service); updateStatus (""); - + // We want to monitor the service for as long as we are // connected to it. try { mService.registerCallback(mCallback); + + + if (autoStartOnBind) + { + autoStartOnBind = false; + + startTor(); + + } + } catch (RemoteException e) { // In this case the service has crashed before we could even // do anything with it; we can count on soon being // disconnected (and then reconnected if it can be restarted) // so there is no need to do anything here. + Log.i(TAG,"error registering callback to service",e); } - + } @@ -844,8 +1053,6 @@ public class Orbot extends Activity implements OnClickListener, TorConstants try { mService.unregisterCallback(mCallback); - - } catch (RemoteException e) { // There is nothing special we need to do if the service // has crashed. diff --git a/src/org/torproject/android/SettingsPreferences.java b/src/org/torproject/android/SettingsPreferences.java index d2f0792c..aa92c30b 100644 --- a/src/org/torproject/android/SettingsPreferences.java +++ b/src/org/torproject/android/SettingsPreferences.java @@ -3,7 +3,6 @@ package org.torproject.android; -import org.torproject.android.service.TorServiceUtils; import org.torproject.android.service.TorTransProxy; import android.content.Intent; @@ -21,8 +20,7 @@ public class SettingsPreferences private CheckBoxPreference prefCBTransProxy = null; private CheckBoxPreference prefcBTransProxyAll = null; private Preference prefTransProxyApps = null; - private Preference prefWebProxy = null; - + private CheckBoxPreference prefHiddenServices = null; private boolean hasRoot = false; @@ -37,7 +35,6 @@ public class SettingsPreferences } - @Override protected void onResume() { @@ -63,10 +60,13 @@ public class SettingsPreferences } - //disabled for now 28/07 nf - //prefWebProxy = ((PreferenceCategory)this.getPreferenceScreen().getPreference(1)).getPreference(0); - //prefWebProxy.setOnPreferenceClickListener(this); - } + prefHiddenServices = ((CheckBoxPreference)((PreferenceCategory)this.getPreferenceScreen().getPreference(4)).getPreference(0)); + prefHiddenServices.setOnPreferenceClickListener(this); + ((PreferenceCategory)this.getPreferenceScreen().getPreference(4)).getPreference(1).setEnabled(prefHiddenServices.isChecked()); + ((PreferenceCategory)this.getPreferenceScreen().getPreference(4)).getPreference(2).setEnabled(prefHiddenServices.isChecked()); + + + }; @@ -89,18 +89,13 @@ public class SettingsPreferences { startActivity(new Intent(this, AppManager.class)); } - /* - else if (preference == prefWebProxy) + else if (preference == prefHiddenServices) { - Intent intent = new Intent(); - intent.setClassName(this,"com.android.settings.ProxySelector"); - intent.putExtra("title", "Set host=127.0.0.1 and port=8118"); - intent.putExtra("button-label", "Save"); - - startActivity(intent); - - - }*/ + + ((PreferenceCategory)this.getPreferenceScreen().getPreference(4)).getPreference(1).setEnabled(prefHiddenServices.isChecked()); + ((PreferenceCategory)this.getPreferenceScreen().getPreference(4)).getPreference(2).setEnabled(prefHiddenServices.isChecked()); + + } else { prefcBTransProxyAll.setEnabled(prefCBTransProxy.isChecked()); diff --git a/src/org/torproject/android/service/TorBinaryInstaller.java b/src/org/torproject/android/service/TorBinaryInstaller.java index b4543f77..e48c4fd4 100644 --- a/src/org/torproject/android/service/TorBinaryInstaller.java +++ b/src/org/torproject/android/service/TorBinaryInstaller.java @@ -37,11 +37,8 @@ public class TorBinaryInstaller implements TorServiceConstants { boolean privoxyBinaryExists = new File(installPath + PRIVOXY_ASSET_KEY).exists(); Log.i(TAG,"Privoxy binary exists=" + privoxyBinaryExists); - - boolean iptablesBinaryExists = new File(installPath + IPTABLES_ASSET_KEY).exists(); - Log.i(TAG,"IPTables binary exists=" + iptablesBinaryExists); - - if (!(torBinaryExists && privoxyBinaryExists && iptablesBinaryExists) || force) + + if (!(torBinaryExists && privoxyBinaryExists) || force) installFromZip (); } @@ -72,9 +69,6 @@ public class TorBinaryInstaller implements TorServiceConstants { zipen = zip.getEntry(ASSETS_BASE + PRIVOXYCONFIG_ASSET_KEY); streamToFile(zip.getInputStream(zipen),installPath + PRIVOXYCONFIG_ASSET_KEY); - zipen = zip.getEntry(ASSETS_BASE + IPTABLES_ASSET_KEY); - streamToFile(zip.getInputStream(zipen),installPath + IPTABLES_ASSET_KEY); - zip.close(); diff --git a/src/org/torproject/android/service/TorService.java b/src/org/torproject/android/service/TorService.java index 9f165049..43277bca 100644 --- a/src/org/torproject/android/service/TorService.java +++ b/src/org/torproject/android/service/TorService.java @@ -401,13 +401,11 @@ public class TorService extends Service implements TorServiceConstants, Runnable torBinaryPath = appHome + TOR_BINARY_ASSET_KEY; privoxyPath = appHome + PRIVOXY_ASSET_KEY; - String iptablesPath = appHome + IPTABLES_ASSET_KEY; boolean torBinaryExists = new File(torBinaryPath).exists(); boolean privoxyBinaryExists = new File(privoxyPath).exists(); - boolean iptablesBinaryExists = new File(iptablesPath).exists(); - - if (!(torBinaryExists && privoxyBinaryExists && iptablesBinaryExists)) + + if (!(torBinaryExists && privoxyBinaryExists)) { killTorProcess (); @@ -417,7 +415,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable torBinaryExists = new File(torBinaryPath).exists(); privoxyBinaryExists = new File(privoxyPath).exists(); - if (torBinaryExists && privoxyBinaryExists && iptablesBinaryExists) + if (torBinaryExists && privoxyBinaryExists) { logNotice(getString(R.string.status_install_success)); @@ -439,9 +437,8 @@ public class TorService extends Service implements TorServiceConstants, Runnable { logNotice("Found Tor binary: " + torBinaryPath); - logNotice("Found privoxy binary: " + privoxyPath); + logNotice("Found Privoxy binary: " + privoxyPath); - logNotice("Found iptables binary: " + iptablesPath); } @@ -455,9 +452,6 @@ public class TorService extends Service implements TorServiceConstants, Runnable String[] cmd2 = {SHELL_CMD_CHMOD + ' ' + CHMOD_EXE_VALUE + ' ' + privoxyPath}; TorServiceUtils.doShellCommand(cmd2, log, false, true); - logNotice("(re)Setting permission on iptables binary"); - String[] cmd3 = {SHELL_CMD_CHMOD + ' ' + CHMOD_EXE_VALUE + ' ' + iptablesPath}; - TorServiceUtils.doShellCommand(cmd3, log, false, true); return true; } @@ -947,8 +941,17 @@ public class TorService extends Service implements TorServiceConstants, Runnable { //turn on - - return setupTransProxy(currentStatus == STATUS_ON); + try + { + setupTransProxy(currentStatus == STATUS_ON); + return true; + } + catch (Exception e) + { + Log.i(TAG, "error enabling transproxy",e); + + return false; + } } public String getConfiguration (String name) @@ -993,7 +996,19 @@ public class TorService extends Service implements TorServiceConstants, Runnable if (configBuffer == null) configBuffer = new ArrayList(); - configBuffer.add(name + ' ' + value); + if (value == null || value.length() == 0) + { + if (conn != null) + { + try { + conn.resetConf(Arrays.asList(new String[]{name})); + } catch (IOException e) { + Log.w(TAG, "Unable to reset conf",e); + } + } + } + else + configBuffer.add(name + ' ' + value); return false; } @@ -1007,7 +1022,6 @@ public class TorService extends Service implements TorServiceConstants, Runnable if (configBuffer != null) { conn.setConf(configBuffer); - //conn.saveConf(); configBuffer = null; } @@ -1083,6 +1097,8 @@ public class TorService extends Service implements TorServiceConstants, Runnable boolean ReachableAddresses = prefs.getBoolean(TorConstants.PREF_REACHABLE_ADDRESSES,false); + boolean enableHiddenServices = prefs.getBoolean("pref_hs_enable", false); + boolean enableTransparentProxy = prefs.getBoolean(TorConstants.PREF_TRANSPARENT, false); @@ -1137,6 +1153,10 @@ public class TorService extends Service implements TorServiceConstants, Runnable mBinder.updateConfiguration("ReachableAddresses", ReachableAddressesPorts, false); } + else + { + mBinder.updateConfiguration("ReachableAddresses", "", false); + } } catch (Exception e) { @@ -1155,6 +1175,12 @@ public class TorService extends Service implements TorServiceConstants, Runnable mBinder.updateConfiguration("ExitPolicy", "reject *:*", false); } + else + { + mBinder.updateConfiguration("ORPort", "", false); + mBinder.updateConfiguration("Nickname", "", false); + mBinder.updateConfiguration("ExitPolicy", "", false); + } } catch (Exception e) { @@ -1163,12 +1189,41 @@ public class TorService extends Service implements TorServiceConstants, Runnable return; } + if (enableHiddenServices) + { + mBinder.updateConfiguration("HiddenServiceDir","/data/data/org.torproject.android/", false); + + String hsPorts = prefs.getString("pref_hs_ports",""); + + StringTokenizer st = new StringTokenizer (hsPorts,","); + String hsPortConfig = null; + + while (st.hasMoreTokens()) + { + hsPortConfig = st.nextToken(); + + if (hsPortConfig.indexOf(":")==-1) //setup the port to localhost if not specifed + { + hsPortConfig = hsPortConfig + " 127.0.0.1:" + hsPortConfig; + } + + mBinder.updateConfiguration("HiddenServicePort",hsPortConfig, false); + } + + + } + else + { + mBinder.updateConfiguration("HiddenServiceDir","", false); + + } + mBinder.saveConfiguration(); } - private boolean setupTransProxy (boolean enabled) + private boolean setupTransProxy (boolean enabled) throws Exception { SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplication()); @@ -1187,27 +1242,19 @@ public class TorService extends Service implements TorServiceConstants, Runnable if (hasRoot && enableTransparentProxy) { - try - { - TorTransProxy.setDNSProxying(); - boolean success = TorTransProxy.setTransparentProxyingByApp(this,AppManager.getApps(this),transProxyAll); - logNotice ("TorTransProxy enabled: " + success); + //TorTransProxy.setDNSProxying(); + int code = TorTransProxy.setTransparentProxyingByApp(this,AppManager.getApps(this),transProxyAll); + + logNotice ("TorTransProxy resp code: " + code); return true; - - } catch (Exception e) { - - logNotice("WARNING: Error configuring transparenty proxying: " + e.getMessage()); - Log.w(TAG, "error refreshing iptables: err=" + e.getMessage(), e); - - return false; - } + } else { - TorTransProxy.purgeIptables(); + TorTransProxy.purgeIptables(this,AppManager.getApps(this)); } } @@ -1215,7 +1262,7 @@ public class TorService extends Service implements TorServiceConstants, Runnable { if (hasRoot) { - TorTransProxy.purgeIptables(); + TorTransProxy.purgeIptables(this,AppManager.getApps(this)); } } diff --git a/src/org/torproject/android/service/TorServiceConstants.java b/src/org/torproject/android/service/TorServiceConstants.java index dd962b81..5b357046 100644 --- a/src/org/torproject/android/service/TorServiceConstants.java +++ b/src/org/torproject/android/service/TorServiceConstants.java @@ -26,9 +26,6 @@ public interface TorServiceConstants { //privoxy.config public final static String PRIVOXYCONFIG_ASSET_KEY = "privoxy.config"; - - //iptables key - public final static String IPTABLES_ASSET_KEY = "iptables"; //various console cmds public final static String SHELL_CMD_CHMOD = "chmod"; diff --git a/src/org/torproject/android/service/TorTransProxy.java b/src/org/torproject/android/service/TorTransProxy.java index 6787ae51..29e4b5a1 100644 --- a/src/org/torproject/android/service/TorTransProxy.java +++ b/src/org/torproject/android/service/TorTransProxy.java @@ -20,6 +20,7 @@ public class TorTransProxy { private final static String IPTABLES_ADD = " -A "; + //private final static String CMD_DNS_PROXYING_DELETE = "iptables -t nat -D PREROUTING -p udp --dport 53 -j DNAT --to 127.0.0.1:5400 || exit\n"; // - just calling a system wide flush of iptables rules //private final static String IPTABLES_DELETE = " -D "; //not deleting manually anymore - just calling a system wide flush of iptables rules @@ -51,6 +52,40 @@ public class TorTransProxy { return false; } + /** + * Check if we have root access + * @return boolean true if we have root + */ + public static String getIPTablesVersion() { + + + StringBuilder log = new StringBuilder(); + + try { + + // Run an empty script just to check root access + String[] cmd = {"iptables -v"}; + int exitCode = TorServiceUtils.doShellCommand(cmd, log, true, true); + + String out = log.toString(); + if (out.indexOf(" v")!=-1) + { + + out = out.substring(out.indexOf(" v")+2); + out = out.substring(0,out.indexOf(":")); + + return out; + } + + + } catch (Exception e) { + Log.w(TAG,"Error checking iptables version: " + e.getMessage() ,e); + } + + Log.w(TAG, "Could not acquire check iptables: " + log.toString()); + return null; + } + private static String findBaseDir () { /* @@ -72,10 +107,11 @@ public class TorTransProxy { return BASE_DIR; }*/ - return "/system/bin/"; + return ""; } + /* public static int setDNSProxying () throws Exception { String baseDir = findBaseDir(); @@ -91,7 +127,7 @@ public class TorTransProxy { return code; - } + }*/ /* public static int setIptablesDropAll() { @@ -109,6 +145,45 @@ public class TorTransProxy { } */ + public static int purgeIptables(Context context, TorifiedApp[] apps) throws Exception { + + String baseDir = findBaseDir(); + + + final StringBuilder script = new StringBuilder(); + + StringBuilder res = new StringBuilder(); + int code = -1; + + for (int i = 0; i < apps.length; i++) + { + + //flush nat for every app + script.append(baseDir); + script.append("iptables -t nat -m owner --uid-owner "); + script.append(apps[i].getUid()); + script.append(" -F || exit\n"); + script.append("iptables -t filter -m owner --uid-owner "); + script.append(apps[i].getUid()); + script.append(" -F || exit\n"); + + } + + + String[] cmd = {script.toString()}; + Log.i(TAG, cmd[0]); + + code = TorServiceUtils.doShellCommand(cmd, res, true, true); + + String msg = res.toString(); + Log.i(TAG, msg); + + + return code; + + } + + /* public static boolean purgeIptables() { String baseDir = findBaseDir(); @@ -129,96 +204,121 @@ public class TorTransProxy { Log.w(TAG,"error purging iptables: " + e); return false; } - } + }*/ - public static boolean setTransparentProxyingByApp(Context context, TorifiedApp[] apps, boolean forceAll) throws Exception + public static int setTransparentProxyingByApp(Context context, TorifiedApp[] apps, boolean forceAll) throws Exception { String baseDir = findBaseDir(); - String command = null; + String iptablesVersion = getIPTablesVersion(); + Log.i(TAG, "iptables version: " + iptablesVersion); - command = IPTABLES_ADD; //ADD + boolean ipTablesOld = false; + if (iptablesVersion != null && iptablesVersion.startsWith("1.3")){ + ipTablesOld = true; + } - final StringBuilder script = new StringBuilder(); + StringBuilder script = new StringBuilder(); - //first we have to flush old settings - script.append(baseDir); - script.append(CMD_NAT_FLUSH); - script.append(" || exit\n"); - - script.append(baseDir); - script.append(CMD_FILTER_FLUSH); - script.append(" || exit\n"); - StringBuilder res = new StringBuilder(); int code = -1; - for (int i = 0; i < apps.length; i++) + for (int i = 0; i < apps.length; i++) + { + + //flush nat for every app + script.append(baseDir); + script.append("iptables -t nat -m owner --uid-owner "); + script.append(apps[i].getUid()); + script.append(" -F || exit\n"); + script.append("iptables -t filter -m owner --uid-owner "); + script.append(apps[i].getUid()); + script.append(" -F || exit\n"); + + } + + String[] cmdFlush = {script.toString()}; + Log.i(TAG, cmdFlush[0]); + + code = TorServiceUtils.doShellCommand(cmdFlush, res, true, true); + + String msg = res.toString(); + Log.i(TAG, msg); + + script = new StringBuilder(); + + for (int i = 0; i < apps.length; i++) + { + + if (forceAll || apps[i].isTorified()) { - if (forceAll || apps[i].isTorified()) + + if (apps[i].getUsername().equals(TorServiceConstants.TOR_APP_USERNAME)) { + Log.i(TAG,"detected Orbot app - will not transproxy"); - if (apps[i].getUsername().equals(TorServiceConstants.TOR_APP_USERNAME)) - { - Log.i(TAG,"detected Orbot app - will not transproxy"); - - continue; - } - - Log.i(TAG,"enabling transproxy for app: " + apps[i].getUsername() + "(" + apps[i].getUid() + ")"); - - - - //TCP + continue; + } + + Log.i(TAG,"enabling transproxy for app: " + apps[i].getUsername() + "(" + apps[i].getUid() + ")"); + + + //TCP + script.append(baseDir); + script.append("iptables -t nat"); + script.append(" -A OUTPUT -p tcp --syn"); + script.append(" -m owner --uid-owner "); + script.append(apps[i].getUid()); + script.append(" -m tcp "); + + if (ipTablesOld) + script.append(" -j DNAT --to 127.0.0.1:9040"); + else + script.append(" -j REDIRECT --to-ports 9040"); + + script.append(" || exit\n"); + + //DNS + script.append(baseDir); + script.append("iptables -t nat"); + script.append(" -A OUTPUT -p udp -m owner --uid-owner "); + script.append(apps[i].getUid()); + script.append(" -m udp --dport 53"); + + if (ipTablesOld) + script.append(" -j DNAT --to 127.0.0.1:5400"); + else + script.append(" -j REDIRECT --to-ports 5400"); + + script.append(" || exit\n"); + + //EVERYTHING ELSE UDP - DROP! + if (!ipTablesOld) //for some reason this doesn't work on iptables 1.3.7 + { script.append(baseDir); - script.append("iptables -t nat"); - script.append(" -A OUTPUT -p tcp -m owner --uid-owner "); - script.append(apps[i].getUid()); - // script.append(" -j DNAT --to 127.0.0.1:9040"); - script.append(" -m tcp --syn -j REDIRECT --to-ports 9040"); - script.append(" || exit\n"); - - //UDP - script.append(baseDir); - script.append("iptables -t nat"); + script.append("iptables"); script.append(" -A OUTPUT -p udp -m owner --uid-owner "); script.append(apps[i].getUid()); - script.append(" --dport 53 -j REDIRECT --to-ports 5400"); //drop all UDP packets as Tor won't handle them - script.append(" || exit\n"); - - /* - script.append(baseDir); - script.append("iptables -t nat"); - script.append(" -A OUTPUT -m owner --uid-owner "); - script.append(apps[i].getUid()); script.append(" -j DROP"); //drop all other packets as Tor won't handle them script.append(" || exit\n"); - */ - - - /* - * iptables -t nat -A OUTPUT -p tcp -m owner --uid-owner anonymous -m tcp -j REDIRECT --to-ports 9040 -iptables -t nat -A OUTPUT -p udp -m owner --uid-owner anonymous -m udp --dport 53 -j REDIRECT --to-ports 53 -iptables -t filter -A OUTPUT -p tcp -m owner --uid-owner anonymous -m tcp --dport 9040 -j ACCEPT -iptables -t filter -A OUTPUT -p udp -m owner --uid-owner anonymous -m udp --dport 53 -j ACCEPT -iptables -t filter -A OUTPUT -m owner --uid-owner anonymous -j DROP - - */ - } - } - - - String[] cmd = {script.toString()}; - Log.i(TAG, cmd[0]); - - code = TorServiceUtils.doShellCommand(cmd, res, true, true); - - String msg = res.toString(); - Log.i(TAG, msg); + } + + + } + } + + + String[] cmdAdd = {script.toString()}; + Log.i(TAG, cmdAdd[0]); + + code = TorServiceUtils.doShellCommand(cmdAdd, res, true, true); + + msg = res.toString(); + Log.i(TAG, msg); - return false; + return code; }