From b7339e94236f9ceac09472e1a35c677ecc708670 Mon Sep 17 00:00:00 2001 From: Hans-Christoph Steiner Date: Thu, 22 May 2014 19:51:00 -0400 Subject: [PATCH] support HTTPS:// for local repo in a preference Allow the local repo to use HTTPS:// instead of HTTP://. This is currently default off since handling the self-signed certificate is not currently graceful. In the future, the SPKI that AndroidPinning uses should be included in the repo meta data, then when someone marks a repo as trusted, that local repo's SPKI should be added to the list of trusted keys in AndroidPinning. fixes #2960 https://dev.guardianproject.info/issues/2960 --- res/values/strings.xml | 3 ++ res/xml/preferences.xml | 4 ++ src/org/fdroid/fdroid/Preferences.java | 19 ++++++++ .../fdroid/fdroid/PreferencesActivity.java | 5 +++ .../fdroid/localrepo/LocalRepoService.java | 45 ++++++++++++++----- src/org/fdroid/fdroid/net/LocalHTTPD.java | 33 +++++++------- .../fdroid/net/WifiStateChangeService.java | 6 +-- .../views/QrWizardDownloadActivity.java | 8 +--- 8 files changed, 85 insertions(+), 38 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index c5fc6105e..3dbbbd937 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -39,6 +39,9 @@ Do not advertise your local repo. Name of your Local Repo The advertised title of your local repo: %s + Use Private Connection + Use encrypted HTTPS:// connection for local repo + Use insecure HTTP:// connection for local repo Search Results App Details diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml index fce6f7cb3..9c4f3e65b 100644 --- a/res/xml/preferences.xml +++ b/res/xml/preferences.xml @@ -51,6 +51,10 @@ + updateHistoryListeners = new ArrayList(); private List localRepoBonjourListeners = new ArrayList(); private List localRepoNameListeners = new ArrayList(); + private List localRepoHttpsListeners = new ArrayList(); private boolean isInitialized(String key) { return initialized.containsKey(key) && initialized.get(key); @@ -89,6 +92,10 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi return preferences.getBoolean(PREF_LOCAL_REPO_BONJOUR, DEFAULT_LOCAL_REPO_BONJOUR); } + public boolean isLocalRepoHttpsEnabled() { + return preferences.getBoolean(PREF_LOCAL_REPO_HTTPS, DEFAULT_LOCAL_REPO_HTTPS); + } + private String getDefaultLocalRepoName() { return (Build.BRAND + " " + Build.MODEL + String.valueOf(new Random().nextInt(9999))) .replaceAll(" ", "-"); @@ -178,6 +185,10 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi for ( ChangeListener listener : localRepoNameListeners ) { listener.onPreferenceChange(); } + } else if (key.equals(PREF_LOCAL_REPO_HTTPS)) { + for ( ChangeListener listener : localRepoHttpsListeners ) { + listener.onPreferenceChange(); + } } } @@ -205,6 +216,14 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi localRepoNameListeners.remove(listener); } + public void registerLocalRepoHttpsListeners(ChangeListener listener) { + localRepoHttpsListeners.add(listener); + } + + public void unregisterLocalRepoHttpsListeners(ChangeListener listener) { + localRepoHttpsListeners.remove(listener); + } + public static interface ChangeListener { public void onPreferenceChange(); } diff --git a/src/org/fdroid/fdroid/PreferencesActivity.java b/src/org/fdroid/fdroid/PreferencesActivity.java index 15e532e4a..9bf66d4b0 100644 --- a/src/org/fdroid/fdroid/PreferencesActivity.java +++ b/src/org/fdroid/fdroid/PreferencesActivity.java @@ -56,6 +56,7 @@ public class PreferencesActivity extends PreferenceActivity implements Preferences.PREF_IGN_TOUCH, Preferences.PREF_LOCAL_REPO_BONJOUR, Preferences.PREF_LOCAL_REPO_NAME, + Preferences.PREF_LOCAL_REPO_HTTPS, Preferences.PREF_CACHE_APK, Preferences.PREF_EXPERT, Preferences.PREF_ROOT_INSTALLER, @@ -154,6 +155,10 @@ public class PreferencesActivity extends PreferenceActivity implements } else if (key.equals(Preferences.PREF_LOCAL_REPO_NAME)) { textSummary(key, R.string.local_repo_name_summary); + } else if (key.equals(Preferences.PREF_LOCAL_REPO_HTTPS)) { + onoffSummary(key, R.string.local_repo_https_on, + R.string.local_repo_https_off); + } else if (key.equals(Preferences.PREF_CACHE_APK)) { onoffSummary(key, R.string.cache_downloaded_on, R.string.cache_downloaded_off); diff --git a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java index 3e783493b..fedf2126d 100644 --- a/src/org/fdroid/fdroid/localrepo/LocalRepoService.java +++ b/src/org/fdroid/fdroid/localrepo/LocalRepoService.java @@ -5,15 +5,12 @@ import android.annotation.SuppressLint; import android.app.*; import android.content.*; import android.os.*; -import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; -import org.fdroid.fdroid.FDroidApp; -import org.fdroid.fdroid.Preferences; +import org.fdroid.fdroid.*; import org.fdroid.fdroid.Preferences.ChangeListener; -import org.fdroid.fdroid.R; import org.fdroid.fdroid.net.LocalHTTPD; import org.fdroid.fdroid.net.WifiStateChangeService; import org.fdroid.fdroid.views.LocalRepoActivity; @@ -91,6 +88,23 @@ public class LocalRepoService extends Service { } }; + private ChangeListener localRepoHttpsChangeListener = new ChangeListener() { + @Override + public void onPreferenceChange() { + Log.i("localRepoHttpsChangeListener", "onPreferenceChange"); + if (localHttpd.isAlive()) { + new AsyncTask() { + @Override + protected Void doInBackground(Void... params) { + stopNetworkServices(); + startNetworkServices(); + return null; + } + }.execute(); + } + } + }; + @Override public void onCreate() { notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); @@ -137,23 +151,25 @@ public class LocalRepoService extends Service { startWebServer(); if (Preferences.get().isLocalRepoBonjourEnabled()) registerMDNSService(); + Preferences.get().registerLocalRepoHttpsListeners(localRepoHttpsChangeListener); } private void stopNetworkServices() { + Preferences.get().unregisterLocalRepoHttpsListeners(localRepoHttpsChangeListener); unregisterMDNSService(); stopWebServer(); } private void startWebServer() { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - Runnable webServer = new Runnable() { // Tell Eclipse this is not a leak because of Looper use. @SuppressLint("HandlerLeak") @Override public void run() { - localHttpd = new LocalHTTPD(getFilesDir(), - prefs.getBoolean("use_https", false)); + localHttpd = new LocalHTTPD( + LocalRepoService.this, + getFilesDir(), + Preferences.get().isLocalRepoHttpsEnabled()); Looper.prepare(); // must be run before creating a Handler webServerThreadHandler = new Handler() { @@ -200,11 +216,16 @@ public class LocalRepoService extends Service { final HashMap values = new HashMap(); values.put("path", "/fdroid/repo"); values.put("name", repoName); - // TODO set type based on "use HTTPS" pref values.put("fingerprint", FDroidApp.repo.fingerprint); - values.put("type", "fdroidrepo"); - pairService = ServiceInfo.create("_http._tcp.local.", - repoName, FDroidApp.port, 0, 0, values); + String type; + if (Preferences.get().isLocalRepoHttpsEnabled()) { + values.put("type", "fdroidrepos"); + type = "_https._tcp.local."; + } else { + values.put("type", "fdroidrepo"); + type = "_http._tcp.local."; + } + pairService = ServiceInfo.create(type, repoName, FDroidApp.port, 0, 0, values); new Thread(new Runnable() { @Override diff --git a/src/org/fdroid/fdroid/net/LocalHTTPD.java b/src/org/fdroid/fdroid/net/LocalHTTPD.java index 499da4967..eea5ef5d9 100644 --- a/src/org/fdroid/fdroid/net/LocalHTTPD.java +++ b/src/org/fdroid/fdroid/net/LocalHTTPD.java @@ -1,39 +1,33 @@ package org.fdroid.fdroid.net; +import android.content.Context; import android.util.Log; import android.webkit.MimeTypeMap; import fi.iki.elonen.NanoHTTPD; import org.fdroid.fdroid.FDroidApp; +import org.fdroid.fdroid.localrepo.LocalRepoKeyStore; -import java.io.File; -import java.io.FileInputStream; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.io.UnsupportedEncodingException; +import java.io.*; import java.net.URLEncoder; -import java.util.Arrays; -import java.util.Collections; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.StringTokenizer; +import java.util.*; import javax.net.ssl.SSLServerSocketFactory; public class LocalHTTPD extends NanoHTTPD { private static final String TAG = LocalHTTPD.class.getCanonicalName(); + private final Context context; private final File webRoot; private final boolean logRequests; - public LocalHTTPD(File webRoot, boolean useHttps) { + public LocalHTTPD(Context context, File webRoot, boolean useHttps) { super(FDroidApp.ipAddressString, FDroidApp.port); this.logRequests = false; this.webRoot = webRoot; + this.context = context; if (useHttps) enableHTTPS(); } @@ -91,7 +85,15 @@ public class LocalHTTPD extends NanoHTTPD { } private void enableHTTPS() { - // TODO copy implementation from Kerplapp + try { + LocalRepoKeyStore localRepoKeyStore = LocalRepoKeyStore.get(context); + SSLServerSocketFactory factory = NanoHTTPD.makeSSLSocketFactory( + localRepoKeyStore.getKeyStore(), + localRepoKeyStore.getKeyManagers()); + makeSecure(factory); + } catch (IOException e) { + e.printStackTrace(); + } } private Response respond(Map headers, String uri) { @@ -305,7 +307,8 @@ public class LocalHTTPD extends NanoHTTPD { } for (String directory : directories) { String dir = directory + "/"; - msg.append("
  • ").append(dir) .append("
  • "); } diff --git a/src/org/fdroid/fdroid/net/WifiStateChangeService.java b/src/org/fdroid/fdroid/net/WifiStateChangeService.java index f7cd2e111..cecfb1c42 100644 --- a/src/org/fdroid/fdroid/net/WifiStateChangeService.java +++ b/src/org/fdroid/fdroid/net/WifiStateChangeService.java @@ -3,12 +3,10 @@ package org.fdroid.fdroid.net; import android.app.Service; import android.content.Intent; -import android.content.SharedPreferences; import android.net.wifi.WifiInfo; import android.net.wifi.WifiManager; import android.os.AsyncTask; import android.os.IBinder; -import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; @@ -59,9 +57,7 @@ public class WifiStateChangeService extends Service { FDroidApp.bssid = wifiInfo.getBSSID(); String scheme; - // TODO move this to Preferences.get().isHttpsEnabled(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(WifiStateChangeService.this); - if (prefs.getBoolean("use_https", false)) + if (Preferences.get().isLocalRepoHttpsEnabled()) scheme = "https"; else scheme = "http"; diff --git a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java index ffe90fae9..2952be63a 100644 --- a/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java +++ b/src/org/fdroid/fdroid/views/QrWizardDownloadActivity.java @@ -5,7 +5,6 @@ import android.app.Activity; import android.content.*; import android.os.Build; import android.os.Bundle; -import android.preference.PreferenceManager; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import android.view.View; @@ -13,9 +12,7 @@ import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; -import org.fdroid.fdroid.FDroidApp; -import org.fdroid.fdroid.QrGenAsyncTask; -import org.fdroid.fdroid.R; +import org.fdroid.fdroid.*; import org.fdroid.fdroid.net.WifiStateChangeService; public class QrWizardDownloadActivity extends Activity { @@ -62,9 +59,8 @@ public class QrWizardDownloadActivity extends Activity { }; private void resetNetworkInfo() { - final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); String qrString = ""; - if (prefs.getBoolean("use_https", false)) + if (Preferences.get().isLocalRepoHttpsEnabled()) qrString += "https"; else qrString += "http";