Re-enable NetCipher to provide decent TLS on all Android versions

Revert "Revert netcipher to fix SNI regression"
This reverts commit 6c8e726aad.

NetCipher is important for making sure that the client running on older
versions of Android is not using extremely crappy default TLS settings.  It
also streamlines the Tor support.

closes #431 https://gitlab.com/fdroid/fdroidclient/issues/431
closes #576 https://gitlab.com/fdroid/fdroidclient/issues/576
This commit is contained in:
Hans-Christoph Steiner 2016-02-26 19:48:07 +01:00
parent eed0070d4b
commit 38e4b38602
13 changed files with 120 additions and 42 deletions

View File

@ -1,3 +1,8 @@
* add simple "Use Tor" preference
* Enable TLS v1.2 for officials repo on all devices that support it
### 0.98.1 (2016-02-14)
* Fix crash when entering only a space into the search dialog

View File

@ -17,6 +17,7 @@ dependencies {
compile 'com.google.zxing:core:3.2.1'
compile 'eu.chainfire:libsuperuser:1.0.0.201602011018'
compile 'cc.mvdan.accesspoint:library:0.1.3'
compile 'info.guardianproject.netcipher:netcipher:1.2.1'
compile 'commons-net:commons-net:3.4'
compile 'org.openhab.jmdns:jmdns:3.4.2'
compile('ch.acra:acra:4.8.2') {
@ -71,6 +72,7 @@ if (!hasProperty('sourceDeps')) {
'eu.chainfire:libsuperuser:952c5fc82f9c31d31d2b6a7054ee267dac1685fb037a254888c73c48de661eaf',
'cc.mvdan.accesspoint:library:dc89a085d6bc40381078b8dd7776b12bde0dbaf8ffbcddb17ec4ebc3edecc7ba',
'commons-net:commons-net:38cf2eca826b8bcdb236fc1f2e79e0c6dd8e7e0f5c44a3b8e839a1065b2fbe2e',
'info.guardianproject.netcipher:netcipher:611ec5bde9d799fd57e1efec5c375f9f460de2cdda98918541decc9a7d02f2ad',
'org.openhab.jmdns:jmdns:7a4b34b5606bbd2aff7fdfe629edcb0416fccd367fb59a099f210b9aba4f0bce',
'com.madgag.spongycastle:pkix:6aba9b2210907a3d46dd3dcac782bb3424185290468d102d5207ebdc9796a905',
'com.madgag.spongycastle:prov:029f26cd6b67c06ffa05702d426d472c141789001bcb15b7262ed86c868e5643',

View File

@ -10,6 +10,10 @@
-dontnote android.support.**
-dontnote **ILicensingService
# StrongHttpsClient and its support classes are totally unused, so the
# ch.boye.httpclientandroidlib.** classes are also unneeded
-dontwarn info.guardianproject.netcipher.client.**
# These libraries are known to break if minification is enabled on them. They
# use reflection to instantiate classes, for example. If the keep flags are
# removed, proguard will strip classes which are required, which may result in

View File

@ -166,6 +166,8 @@
<string name="next">Next</string>
<string name="skip">Skip</string>
<string name="useTor">Use Tor</string>
<string name="useTorSummary">Force download traffic through Tor for increased privacy</string>
<string name="proxy">Proxy</string>
<string name="enable_proxy_title">Enable HTTP Proxy</string>
<string name="enable_proxy_summary">Configure HTTP Proxy for all network requests</string>

View File

@ -48,6 +48,10 @@
android:title="@string/local_repo_name" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/proxy" >
<CheckBoxPreference
android:key="useTor"
android:summary="@string/useTorSummary"
android:title="@string/useTor" />
<CheckBoxPreference
android:defaultValue="false"
android:key="enableProxy"

View File

@ -137,6 +137,7 @@ public class FDroid extends AppCompatActivity implements SearchView.OnQueryTextL
@Override
protected void onResume() {
super.onResume();
FDroidApp.checkStartTor(this);
// AppDetails and RepoDetailsActivity set different NFC actions, so reset here
NfcHelper.setAndroidBeam(this, getApplication().getPackageName());
checkForAddRepoIntent(getIntent());

View File

@ -63,6 +63,8 @@ import java.net.URLStreamHandlerFactory;
import java.security.Security;
import java.util.Locale;
import info.guardianproject.netcipher.NetCipher;
import info.guardianproject.netcipher.proxy.OrbotHelper;
import sun.net.www.protocol.bluetooth.Handler;
@ReportsCrashes(mailTo = "reports@f-droid.org",
@ -296,6 +298,8 @@ public class FDroidApp extends Application {
startService(new Intent(FDroidApp.this, WifiStateChangeService.class));
}
});
configureTor(Preferences.get().isTorEnabled());
}
@TargetApi(18)
@ -352,4 +356,28 @@ public class FDroidApp extends Application {
}
}
}
private static boolean useTor;
/**
* Set the proxy settings based on whether Tor should be enabled or not.
*/
public static void configureTor(boolean enabled) {
useTor = enabled;
if (useTor) {
NetCipher.useTor();
} else {
NetCipher.clearProxy();
}
}
public static void checkStartTor(Context context) {
if (useTor) {
OrbotHelper.requestStartTor(context);
}
}
public static boolean isUsingTor() {
return useTor;
}
}

View File

@ -25,9 +25,11 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
private static final String TAG = "Preferences";
private final Context context;
private final SharedPreferences preferences;
private Preferences(Context context) {
this.context = context;
preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.registerOnSharedPreferenceChangeListener(this);
if (preferences.getString(PREF_LOCAL_REPO_NAME, null) == null) {
@ -54,6 +56,7 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
public static final String PREF_LOCAL_REPO_NAME = "localRepoName";
public static final String PREF_LOCAL_REPO_HTTPS = "localRepoHttps";
public static final String PREF_LANGUAGE = "language";
public static final String PREF_USE_TOR = "useTor";
public static final String PREF_ENABLE_PROXY = "enableProxy";
public static final String PREF_PROXY_HOST = "proxyHost";
public static final String PREF_PROXY_PORT = "proxyPort";
@ -161,6 +164,16 @@ public final class Preferences implements SharedPreferences.OnSharedPreferenceCh
return preferences.getString(PREF_LOCAL_REPO_NAME, getDefaultLocalRepoName());
}
/**
* This preference's default is set dynamically based on whether Orbot is
* installed. If Orbot is installed, default to using Tor, the user can still override
*/
public boolean isTorEnabled() {
// TODO enable once Orbot can auto-start after first install
//return preferences.getBoolean(PREF_USE_TOR, OrbotHelper.requestStartTor(context));
return preferences.getBoolean(PREF_USE_TOR, false);
}
public boolean isProxyEnabled() {
return preferences.getBoolean(PREF_ENABLE_PROXY, DEFAULT_ENABLE_PROXY);
}

View File

@ -55,9 +55,6 @@ public class DownloaderFactory {
String macAddress = url.getHost().replace("-", ":");
return new BluetoothDownloader(context, macAddress, url, destFile);
}
if (isOnionAddress(url)) {
return new TorHttpDownloader(context, url, destFile);
}
if (isLocalFile(url)) {
return new LocalFileDownloader(context, url, destFile);
}

View File

@ -1,6 +1,7 @@
package org.fdroid.fdroid.net;
import android.content.Context;
import android.text.TextUtils;
import android.util.Log;
import com.nostra13.universalimageloader.core.download.BaseImageDownloader;
@ -22,8 +23,11 @@ import java.net.Proxy;
import java.net.SocketAddress;
import java.net.URL;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLHandshakeException;
import info.guardianproject.netcipher.NetCipher;
public class HttpDownloader extends Downloader {
private static final String TAG = "HttpDownloader";
@ -32,7 +36,7 @@ public class HttpDownloader extends Downloader {
protected HttpURLConnection connection;
private Credentials credentials;
private int statusCode = -1;
private int statusCode = -1;
HttpDownloader(Context context, URL url, File destFile)
throws FileNotFoundException, MalformedURLException {
@ -52,6 +56,7 @@ public class HttpDownloader extends Downloader {
* checking out that follows redirects up to a certain point. I guess though the correct way
* is probably to check for a loop (keep a list of all URLs redirected to and if you hit the
* same one twice, bail with an exception).
*
* @throws IOException
*/
@Override
@ -92,17 +97,29 @@ public class HttpDownloader extends Downloader {
if (connection != null) {
return;
}
Preferences prefs = Preferences.get();
if (prefs.isProxyEnabled() && !isSwapUrl()) {
SocketAddress sa = new InetSocketAddress(prefs.getProxyHost(), prefs.getProxyPort());
Proxy proxy = new Proxy(Proxy.Type.HTTP, sa);
connection = (HttpURLConnection) sourceUrl.openConnection(proxy);
} else {
if (isSwapUrl()) {
// swap never works with a proxy, its unrouted IP on the same subnet
connection = (HttpURLConnection) sourceUrl.openConnection();
if (credentials != null) {
credentials.authenticate(connection);
} else {
Preferences prefs = Preferences.get();
if (prefs.isProxyEnabled()) {
// if "Use Tor" is set, NetCipher will ignore these proxy settings
SocketAddress sa = new InetSocketAddress(prefs.getProxyHost(), prefs.getProxyPort());
NetCipher.setProxy(new Proxy(Proxy.Type.HTTP, sa));
}
connection = NetCipher.getHttpURLConnection(sourceUrl);
}
// workaround until NetCipher supports HTTPS SNI
// https://gitlab.com/fdroid/fdroidclient/issues/431
if (connection instanceof HttpsURLConnection
&& !TextUtils.equals(sourceUrl.getHost(), "f-droid.org")
&& !TextUtils.equals(sourceUrl.getHost(), "guardianproject.info")) {
((HttpsURLConnection) connection).setSSLSocketFactory(HttpsURLConnection.getDefaultSSLSocketFactory());
}
if (credentials != null) {
credentials.authenticate(connection);
}
}
@ -159,4 +176,4 @@ public class HttpDownloader extends Downloader {
public void close() {
connection.disconnect();
}
}
}

View File

@ -1,28 +0,0 @@
package org.fdroid.fdroid.net;
import android.content.Context;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.Proxy;
import java.net.SocketAddress;
import java.net.URL;
public class TorHttpDownloader extends HttpDownloader {
TorHttpDownloader(Context context, URL url, File destFile)
throws FileNotFoundException, MalformedURLException {
super(context, url, destFile);
}
@Override
protected void setupConnection() throws IOException {
SocketAddress sa = new InetSocketAddress("127.0.0.1", 8118);
Proxy tor = new Proxy(Proxy.Type.HTTP, sa);
connection = (HttpURLConnection) sourceUrl.openConnection(tor);
}
}

View File

@ -127,6 +127,7 @@ public class ManageReposActivity extends ActionBarActivity {
@Override
protected void onResume() {
super.onResume();
FDroidApp.checkStartTor(this);
/* let's see if someone is trying to send us a new repo */
addRepoFromIntent(getIntent());

View File

@ -21,6 +21,9 @@ import org.fdroid.fdroid.PreferencesActivity;
import org.fdroid.fdroid.R;
import org.fdroid.fdroid.installer.PrivilegedInstaller;
import info.guardianproject.netcipher.NetCipher;
import info.guardianproject.netcipher.proxy.OrbotHelper;
public class PreferencesFragment extends PreferenceFragment
implements SharedPreferences.OnSharedPreferenceChangeListener {
@ -43,10 +46,16 @@ public class PreferencesFragment extends PreferenceFragment
Preferences.PREF_PROXY_PORT,
};
private static final int REQUEST_INSTALL_ORBOT = 0x1234;
private CheckBoxPreference enableProxyCheckPref;
private CheckBoxPreference useTorCheckPref;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
useTorCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_USE_TOR);
enableProxyCheckPref = (CheckBoxPreference) findPreference(Preferences.PREF_ENABLE_PROXY);
}
private void checkSummary(String key, int resId) {
@ -279,6 +288,29 @@ public class PreferencesFragment extends PreferenceFragment
initPrivilegedInstallerPreference();
initManagePrivilegedAppPreference();
// this pref's default is dynamically set based on whether Orbot is installed
boolean useTor = Preferences.get().isTorEnabled();
useTorCheckPref.setDefaultValue(useTor);
useTorCheckPref.setChecked(useTor);
useTorCheckPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object enabled) {
if ((Boolean) enabled) {
final Activity activity = getActivity();
enableProxyCheckPref.setEnabled(false);
if (OrbotHelper.isOrbotInstalled(activity)) {
NetCipher.useTor();
} else {
Intent intent = OrbotHelper.getOrbotInstallIntent(activity);
activity.startActivityForResult(intent, REQUEST_INSTALL_ORBOT);
}
} else {
enableProxyCheckPref.setEnabled(true);
NetCipher.clearProxy();
}
return true;
}
});
}
@Override