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
This commit is contained in:
parent
5f2efbb72a
commit
b7339e9423
@ -39,6 +39,9 @@
|
|||||||
<string name="local_repo_bonjour_off">Do not advertise your local repo.</string>
|
<string name="local_repo_bonjour_off">Do not advertise your local repo.</string>
|
||||||
<string name="local_repo_name">Name of your Local Repo</string>
|
<string name="local_repo_name">Name of your Local Repo</string>
|
||||||
<string name="local_repo_name_summary">The advertised title of your local repo: %s</string>
|
<string name="local_repo_name_summary">The advertised title of your local repo: %s</string>
|
||||||
|
<string name="local_repo_https">Use Private Connection</string>
|
||||||
|
<string name="local_repo_https_on">Use encrypted HTTPS:// connection for local repo</string>
|
||||||
|
<string name="local_repo_https_off">Use insecure HTTP:// connection for local repo</string>
|
||||||
|
|
||||||
<string name="search_results">Search Results</string>
|
<string name="search_results">Search Results</string>
|
||||||
<string name="app_details">App Details</string>
|
<string name="app_details">App Details</string>
|
||||||
|
@ -51,6 +51,10 @@
|
|||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:key="localRepoName"
|
android:key="localRepoName"
|
||||||
android:title="@string/local_repo_name" />
|
android:title="@string/local_repo_name" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:defaultValue="true"
|
||||||
|
android:key="localRepoHttps"
|
||||||
|
android:title="@string/local_repo_https" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
<PreferenceCategory android:title="@string/other">
|
<PreferenceCategory android:title="@string/other">
|
||||||
<CheckBoxPreference android:title="@string/cache_downloaded"
|
<CheckBoxPreference android:title="@string/cache_downloaded"
|
||||||
|
@ -46,6 +46,7 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
public static final String PREF_SYSTEM_INSTALLER = "systemInstaller";
|
public static final String PREF_SYSTEM_INSTALLER = "systemInstaller";
|
||||||
public static final String PREF_LOCAL_REPO_BONJOUR = "localRepoBonjour";
|
public static final String PREF_LOCAL_REPO_BONJOUR = "localRepoBonjour";
|
||||||
public static final String PREF_LOCAL_REPO_NAME = "localRepoName";
|
public static final String PREF_LOCAL_REPO_NAME = "localRepoName";
|
||||||
|
public static final String PREF_LOCAL_REPO_HTTPS = "localRepoHttps";
|
||||||
|
|
||||||
private static final boolean DEFAULT_COMPACT_LAYOUT = false;
|
private static final boolean DEFAULT_COMPACT_LAYOUT = false;
|
||||||
private static final boolean DEFAULT_ROOTED = true;
|
private static final boolean DEFAULT_ROOTED = true;
|
||||||
@ -53,6 +54,7 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
private static final boolean DEFAULT_ROOT_INSTALLER = false;
|
private static final boolean DEFAULT_ROOT_INSTALLER = false;
|
||||||
private static final boolean DEFAULT_SYSTEM_INSTALLER = false;
|
private static final boolean DEFAULT_SYSTEM_INSTALLER = false;
|
||||||
private static final boolean DEFAULT_LOCAL_REPO_BONJOUR = true;
|
private static final boolean DEFAULT_LOCAL_REPO_BONJOUR = true;
|
||||||
|
private static final boolean DEFAULT_LOCAL_REPO_HTTPS = false;
|
||||||
|
|
||||||
private boolean compactLayout = DEFAULT_COMPACT_LAYOUT;
|
private boolean compactLayout = DEFAULT_COMPACT_LAYOUT;
|
||||||
private boolean filterAppsRequiringRoot = DEFAULT_ROOTED;
|
private boolean filterAppsRequiringRoot = DEFAULT_ROOTED;
|
||||||
@ -64,6 +66,7 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
private List<ChangeListener> updateHistoryListeners = new ArrayList<ChangeListener>();
|
private List<ChangeListener> updateHistoryListeners = new ArrayList<ChangeListener>();
|
||||||
private List<ChangeListener> localRepoBonjourListeners = new ArrayList<ChangeListener>();
|
private List<ChangeListener> localRepoBonjourListeners = new ArrayList<ChangeListener>();
|
||||||
private List<ChangeListener> localRepoNameListeners = new ArrayList<ChangeListener>();
|
private List<ChangeListener> localRepoNameListeners = new ArrayList<ChangeListener>();
|
||||||
|
private List<ChangeListener> localRepoHttpsListeners = new ArrayList<ChangeListener>();
|
||||||
|
|
||||||
private boolean isInitialized(String key) {
|
private boolean isInitialized(String key) {
|
||||||
return initialized.containsKey(key) && initialized.get(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);
|
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() {
|
private String getDefaultLocalRepoName() {
|
||||||
return (Build.BRAND + " " + Build.MODEL + String.valueOf(new Random().nextInt(9999)))
|
return (Build.BRAND + " " + Build.MODEL + String.valueOf(new Random().nextInt(9999)))
|
||||||
.replaceAll(" ", "-");
|
.replaceAll(" ", "-");
|
||||||
@ -178,6 +185,10 @@ public class Preferences implements SharedPreferences.OnSharedPreferenceChangeLi
|
|||||||
for ( ChangeListener listener : localRepoNameListeners ) {
|
for ( ChangeListener listener : localRepoNameListeners ) {
|
||||||
listener.onPreferenceChange();
|
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);
|
localRepoNameListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void registerLocalRepoHttpsListeners(ChangeListener listener) {
|
||||||
|
localRepoHttpsListeners.add(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unregisterLocalRepoHttpsListeners(ChangeListener listener) {
|
||||||
|
localRepoHttpsListeners.remove(listener);
|
||||||
|
}
|
||||||
|
|
||||||
public static interface ChangeListener {
|
public static interface ChangeListener {
|
||||||
public void onPreferenceChange();
|
public void onPreferenceChange();
|
||||||
}
|
}
|
||||||
|
@ -56,6 +56,7 @@ public class PreferencesActivity extends PreferenceActivity implements
|
|||||||
Preferences.PREF_IGN_TOUCH,
|
Preferences.PREF_IGN_TOUCH,
|
||||||
Preferences.PREF_LOCAL_REPO_BONJOUR,
|
Preferences.PREF_LOCAL_REPO_BONJOUR,
|
||||||
Preferences.PREF_LOCAL_REPO_NAME,
|
Preferences.PREF_LOCAL_REPO_NAME,
|
||||||
|
Preferences.PREF_LOCAL_REPO_HTTPS,
|
||||||
Preferences.PREF_CACHE_APK,
|
Preferences.PREF_CACHE_APK,
|
||||||
Preferences.PREF_EXPERT,
|
Preferences.PREF_EXPERT,
|
||||||
Preferences.PREF_ROOT_INSTALLER,
|
Preferences.PREF_ROOT_INSTALLER,
|
||||||
@ -154,6 +155,10 @@ public class PreferencesActivity extends PreferenceActivity implements
|
|||||||
} else if (key.equals(Preferences.PREF_LOCAL_REPO_NAME)) {
|
} else if (key.equals(Preferences.PREF_LOCAL_REPO_NAME)) {
|
||||||
textSummary(key, R.string.local_repo_name_summary);
|
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)) {
|
} else if (key.equals(Preferences.PREF_CACHE_APK)) {
|
||||||
onoffSummary(key, R.string.cache_downloaded_on,
|
onoffSummary(key, R.string.cache_downloaded_on,
|
||||||
R.string.cache_downloaded_off);
|
R.string.cache_downloaded_off);
|
||||||
|
@ -5,15 +5,12 @@ import android.annotation.SuppressLint;
|
|||||||
import android.app.*;
|
import android.app.*;
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.os.*;
|
import android.os.*;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.app.NotificationCompat;
|
import android.support.v4.app.NotificationCompat;
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.*;
|
||||||
import org.fdroid.fdroid.Preferences;
|
|
||||||
import org.fdroid.fdroid.Preferences.ChangeListener;
|
import org.fdroid.fdroid.Preferences.ChangeListener;
|
||||||
import org.fdroid.fdroid.R;
|
|
||||||
import org.fdroid.fdroid.net.LocalHTTPD;
|
import org.fdroid.fdroid.net.LocalHTTPD;
|
||||||
import org.fdroid.fdroid.net.WifiStateChangeService;
|
import org.fdroid.fdroid.net.WifiStateChangeService;
|
||||||
import org.fdroid.fdroid.views.LocalRepoActivity;
|
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<Void, Void, Void>() {
|
||||||
|
@Override
|
||||||
|
protected Void doInBackground(Void... params) {
|
||||||
|
stopNetworkServices();
|
||||||
|
startNetworkServices();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}.execute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
|
||||||
@ -137,23 +151,25 @@ public class LocalRepoService extends Service {
|
|||||||
startWebServer();
|
startWebServer();
|
||||||
if (Preferences.get().isLocalRepoBonjourEnabled())
|
if (Preferences.get().isLocalRepoBonjourEnabled())
|
||||||
registerMDNSService();
|
registerMDNSService();
|
||||||
|
Preferences.get().registerLocalRepoHttpsListeners(localRepoHttpsChangeListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopNetworkServices() {
|
private void stopNetworkServices() {
|
||||||
|
Preferences.get().unregisterLocalRepoHttpsListeners(localRepoHttpsChangeListener);
|
||||||
unregisterMDNSService();
|
unregisterMDNSService();
|
||||||
stopWebServer();
|
stopWebServer();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void startWebServer() {
|
private void startWebServer() {
|
||||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
|
|
||||||
Runnable webServer = new Runnable() {
|
Runnable webServer = new Runnable() {
|
||||||
// Tell Eclipse this is not a leak because of Looper use.
|
// Tell Eclipse this is not a leak because of Looper use.
|
||||||
@SuppressLint("HandlerLeak")
|
@SuppressLint("HandlerLeak")
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
localHttpd = new LocalHTTPD(getFilesDir(),
|
localHttpd = new LocalHTTPD(
|
||||||
prefs.getBoolean("use_https", false));
|
LocalRepoService.this,
|
||||||
|
getFilesDir(),
|
||||||
|
Preferences.get().isLocalRepoHttpsEnabled());
|
||||||
|
|
||||||
Looper.prepare(); // must be run before creating a Handler
|
Looper.prepare(); // must be run before creating a Handler
|
||||||
webServerThreadHandler = new Handler() {
|
webServerThreadHandler = new Handler() {
|
||||||
@ -200,11 +216,16 @@ public class LocalRepoService extends Service {
|
|||||||
final HashMap<String, String> values = new HashMap<String, String>();
|
final HashMap<String, String> values = new HashMap<String, String>();
|
||||||
values.put("path", "/fdroid/repo");
|
values.put("path", "/fdroid/repo");
|
||||||
values.put("name", repoName);
|
values.put("name", repoName);
|
||||||
// TODO set type based on "use HTTPS" pref
|
|
||||||
values.put("fingerprint", FDroidApp.repo.fingerprint);
|
values.put("fingerprint", FDroidApp.repo.fingerprint);
|
||||||
values.put("type", "fdroidrepo");
|
String type;
|
||||||
pairService = ServiceInfo.create("_http._tcp.local.",
|
if (Preferences.get().isLocalRepoHttpsEnabled()) {
|
||||||
repoName, FDroidApp.port, 0, 0, values);
|
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() {
|
new Thread(new Runnable() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,39 +1,33 @@
|
|||||||
|
|
||||||
package org.fdroid.fdroid.net;
|
package org.fdroid.fdroid.net;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.webkit.MimeTypeMap;
|
import android.webkit.MimeTypeMap;
|
||||||
|
|
||||||
import fi.iki.elonen.NanoHTTPD;
|
import fi.iki.elonen.NanoHTTPD;
|
||||||
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.FDroidApp;
|
||||||
|
import org.fdroid.fdroid.localrepo.LocalRepoKeyStore;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FilenameFilter;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.StringTokenizer;
|
|
||||||
|
|
||||||
import javax.net.ssl.SSLServerSocketFactory;
|
import javax.net.ssl.SSLServerSocketFactory;
|
||||||
|
|
||||||
public class LocalHTTPD extends NanoHTTPD {
|
public class LocalHTTPD extends NanoHTTPD {
|
||||||
private static final String TAG = LocalHTTPD.class.getCanonicalName();
|
private static final String TAG = LocalHTTPD.class.getCanonicalName();
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
private final File webRoot;
|
private final File webRoot;
|
||||||
private final boolean logRequests;
|
private final boolean logRequests;
|
||||||
|
|
||||||
public LocalHTTPD(File webRoot, boolean useHttps) {
|
public LocalHTTPD(Context context, File webRoot, boolean useHttps) {
|
||||||
super(FDroidApp.ipAddressString, FDroidApp.port);
|
super(FDroidApp.ipAddressString, FDroidApp.port);
|
||||||
this.logRequests = false;
|
this.logRequests = false;
|
||||||
this.webRoot = webRoot;
|
this.webRoot = webRoot;
|
||||||
|
this.context = context;
|
||||||
if (useHttps)
|
if (useHttps)
|
||||||
enableHTTPS();
|
enableHTTPS();
|
||||||
}
|
}
|
||||||
@ -91,7 +85,15 @@ public class LocalHTTPD extends NanoHTTPD {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void enableHTTPS() {
|
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<String, String> headers, String uri) {
|
private Response respond(Map<String, String> headers, String uri) {
|
||||||
@ -305,7 +307,8 @@ public class LocalHTTPD extends NanoHTTPD {
|
|||||||
}
|
}
|
||||||
for (String directory : directories) {
|
for (String directory : directories) {
|
||||||
String dir = directory + "/";
|
String dir = directory + "/";
|
||||||
msg.append("<li><a rel=\"directory\" href=\"").append(encodeUriBetweenSlashes(uri + dir))
|
msg.append("<li><a rel=\"directory\" href=\"")
|
||||||
|
.append(encodeUriBetweenSlashes(uri + dir))
|
||||||
.append("\"><span class=\"dirname\">").append(dir)
|
.append("\"><span class=\"dirname\">").append(dir)
|
||||||
.append("</span></a></b></li>");
|
.append("</span></a></b></li>");
|
||||||
}
|
}
|
||||||
|
@ -3,12 +3,10 @@ package org.fdroid.fdroid.net;
|
|||||||
|
|
||||||
import android.app.Service;
|
import android.app.Service;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.net.wifi.WifiInfo;
|
import android.net.wifi.WifiInfo;
|
||||||
import android.net.wifi.WifiManager;
|
import android.net.wifi.WifiManager;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -59,9 +57,7 @@ public class WifiStateChangeService extends Service {
|
|||||||
FDroidApp.bssid = wifiInfo.getBSSID();
|
FDroidApp.bssid = wifiInfo.getBSSID();
|
||||||
|
|
||||||
String scheme;
|
String scheme;
|
||||||
// TODO move this to Preferences.get().isHttpsEnabled();
|
if (Preferences.get().isLocalRepoHttpsEnabled())
|
||||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(WifiStateChangeService.this);
|
|
||||||
if (prefs.getBoolean("use_https", false))
|
|
||||||
scheme = "https";
|
scheme = "https";
|
||||||
else
|
else
|
||||||
scheme = "http";
|
scheme = "http";
|
||||||
|
@ -5,7 +5,6 @@ import android.app.Activity;
|
|||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceManager;
|
|
||||||
import android.support.v4.content.LocalBroadcastManager;
|
import android.support.v4.content.LocalBroadcastManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@ -13,9 +12,7 @@ import android.view.View.OnClickListener;
|
|||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.fdroid.fdroid.FDroidApp;
|
import org.fdroid.fdroid.*;
|
||||||
import org.fdroid.fdroid.QrGenAsyncTask;
|
|
||||||
import org.fdroid.fdroid.R;
|
|
||||||
import org.fdroid.fdroid.net.WifiStateChangeService;
|
import org.fdroid.fdroid.net.WifiStateChangeService;
|
||||||
|
|
||||||
public class QrWizardDownloadActivity extends Activity {
|
public class QrWizardDownloadActivity extends Activity {
|
||||||
@ -62,9 +59,8 @@ public class QrWizardDownloadActivity extends Activity {
|
|||||||
};
|
};
|
||||||
|
|
||||||
private void resetNetworkInfo() {
|
private void resetNetworkInfo() {
|
||||||
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
|
||||||
String qrString = "";
|
String qrString = "";
|
||||||
if (prefs.getBoolean("use_https", false))
|
if (Preferences.get().isLocalRepoHttpsEnabled())
|
||||||
qrString += "https";
|
qrString += "https";
|
||||||
else
|
else
|
||||||
qrString += "http";
|
qrString += "http";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user