diff --git a/F-Droid/src/javax/jmdns/impl/FDroidServiceInfo.java b/F-Droid/src/javax/jmdns/impl/FDroidServiceInfo.java index c4fba32f3..111354994 100644 --- a/F-Droid/src/javax/jmdns/impl/FDroidServiceInfo.java +++ b/F-Droid/src/javax/jmdns/impl/FDroidServiceInfo.java @@ -2,6 +2,7 @@ package javax.jmdns.impl; import android.os.Parcel; import android.os.Parcelable; +import android.text.TextUtils; import java.net.Inet4Address; import java.net.Inet6Address; @@ -21,8 +22,21 @@ public class FDroidServiceInfo extends ServiceInfoImpl implements Parcelable { super(info); } + /** + * Return the fingerprint of the signing key, or {@code null} if it is not set. + */ public String getFingerprint() { - return getPropertyString("fingerprint"); + // getPropertyString() will return "true" if the value is a zero-length byte array + // so we just do a custom version using getPropertyBytes() + byte[] data = getPropertyBytes("fingerprint"); + if (data == null || data.length == 0) { + return null; + } + String fingerprint = this.readUTF(data, 0, data.length); + if (TextUtils.isEmpty(fingerprint)) { + return null; + } + return fingerprint; } public String getRepoAddress() { diff --git a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java index 17b964e52..b23d28f7e 100644 --- a/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java +++ b/F-Droid/src/org/fdroid/fdroid/RepoUpdater.java @@ -36,6 +36,14 @@ import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +/** + * Handles getting the index metadata for an app repo, then verifying the + * signature on the index metdata, implementing as a JAR signature. + *

+ * WARNING: this class is the central piece of the entire security model of + * FDroid! Avoid modifying it when possible, if you absolutely must, be very, + * very careful with the changes that you are making! + */ public class RepoUpdater { private static final String TAG = "RepoUpdater"; @@ -280,7 +288,8 @@ public class RepoUpdater { * actually in the index.jar itself. If no fingerprint, just store the * signing certificate */ boolean trustNewSigningCertificate = false; - if (TextUtils.isEmpty(repo.fingerprint)) { + // If the fingerprint has never been set, it will be null (never "" or something else) + if (repo.fingerprint == null) { // no info to check things are valid, so just Trust On First Use trustNewSigningCertificate = true; } else { @@ -290,7 +299,8 @@ public class RepoUpdater { && repo.fingerprint.equalsIgnoreCase(fingerprintFromJar)) { trustNewSigningCertificate = true; } else { - throw new UpdateException(repo, "Supplied certificate fingerprint does not match!"); + throw new UpdateException(repo, "Supplied certificate fingerprint does not match: '" + + repo.fingerprint + "' '" + fingerprintFromIndexXml + "' '" + fingerprintFromJar + "'"); } } diff --git a/F-Droid/src/org/fdroid/fdroid/data/Repo.java b/F-Droid/src/org/fdroid/fdroid/data/Repo.java index fb4cbdd06..28979ec6c 100644 --- a/F-Droid/src/org/fdroid/fdroid/data/Repo.java +++ b/F-Droid/src/org/fdroid/fdroid/data/Repo.java @@ -19,13 +19,21 @@ public class Repo extends ValueObject { public String address; public String name; public String description; - public int version; // index version, i.e. what fdroidserver built it - 0 if not specified + /** index version, i.e. what fdroidserver built it - 0 if not specified */ + public int version; public boolean inuse; public int priority; - public String pubkey; // null for an unsigned repo - public String fingerprint; // always null for an unsigned repo - public int maxage; // maximum age of index that will be accepted - 0 for any - public String lastetag; // last etag we updated from, null forces update + /** The signing certificate, {@code null} for a newly added repo */ + public String pubkey; + /** + * The SHA1 fingerprint of {@link #pubkey}, set to {@code null} when a + * newly added repo did not include fingerprint. It should never be an + * empty {@link String}, i.e. {@code ""} */ + public String fingerprint; + /** maximum age of index that will be accepted - 0 for any */ + public int maxage; + /** last etag we updated from, null forces update */ + public String lastetag; public Date lastUpdated; public boolean isSwap; diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/LocalRepoManager.java b/F-Droid/src/org/fdroid/fdroid/localrepo/LocalRepoManager.java index ad23254c8..58b64ef9a 100644 --- a/F-Droid/src/org/fdroid/fdroid/localrepo/LocalRepoManager.java +++ b/F-Droid/src/org/fdroid/fdroid/localrepo/LocalRepoManager.java @@ -345,11 +345,13 @@ public class LocalRepoManager { serializer = XmlPullParserFactory.newInstance().newSerializer(); } - public void build(Writer output) throws IOException, LocalRepoKeyStore.InitException { + public void build(File file) throws IOException, LocalRepoKeyStore.InitException { + Writer output = new FileWriter(file); serializer.setOutput(output); serializer.startDocument(null, null); tagFdroid(); serializer.endDocument(); + output.close(); } private void tagFdroid() throws IOException, LocalRepoKeyStore.InitException { @@ -485,16 +487,12 @@ public class LocalRepoManager { public void writeIndexJar() throws IOException { - FileWriter writer = null; try { - writer = new FileWriter(xmlIndex); - new IndexXmlBuilder(context, apps).build(writer); + new IndexXmlBuilder(context, apps).build(xmlIndex); } catch (Exception e) { Log.e(TAG, "Could not write index jar", e); Toast.makeText(context, R.string.failed_to_create_index, Toast.LENGTH_LONG).show(); return; - } finally { - Utils.closeQuietly(writer); } BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream(xmlIndexJarUnsigned)); diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java index 1e5f9b708..09e9e9998 100644 --- a/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java +++ b/F-Droid/src/org/fdroid/fdroid/localrepo/SwapService.java @@ -259,7 +259,9 @@ public class SwapService extends Service { values.put(RepoProvider.DataColumns.NAME, peer.getName()); values.put(RepoProvider.DataColumns.ADDRESS, peer.getRepoAddress()); values.put(RepoProvider.DataColumns.DESCRIPTION, ""); - values.put(RepoProvider.DataColumns.FINGERPRINT, peer.getFingerprint()); + String fingerprint = peer.getFingerprint(); + if (!TextUtils.isEmpty(fingerprint)) + values.put(RepoProvider.DataColumns.FINGERPRINT, peer.getFingerprint()); values.put(RepoProvider.DataColumns.IN_USE, true); values.put(RepoProvider.DataColumns.IS_SWAP, true); Uri uri = RepoProvider.Helper.insert(this, values); diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java index 45261d6d5..4f29b31ec 100644 --- a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java +++ b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BluetoothPeer.java @@ -7,6 +7,7 @@ import org.fdroid.fdroid.R; import org.fdroid.fdroid.localrepo.type.BluetoothSwap; public class BluetoothPeer implements Peer { + private static final String TAG = "BluetoothPeer"; private BluetoothDevice device; @@ -40,13 +41,15 @@ public class BluetoothPeer implements Peer { } /** - * Bluetooth will exclusively be TOFU. Once a device is connected to a bluetooth socket, - * if we trust it enough to accept a fingerprint from it somehow, then we may as well trust it - * enough to receive an index from it that contains a fingerprint we can use. + * Return the fingerprint of the signing key, or {@code null} if it is not set. + *

+ * This is not yet stored for Bluetooth connections. Once a device is connected to a bluetooth + * socket, if we trust it enough to accept a fingerprint from it somehow, then we may as well + * trust it enough to receive an index from it that contains a fingerprint we can use. */ @Override public String getFingerprint() { - return ""; + return null; } @Override diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java index def234be7..0da35cc0e 100644 --- a/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java +++ b/F-Droid/src/org/fdroid/fdroid/localrepo/peers/BonjourPeer.java @@ -41,6 +41,9 @@ public class BonjourPeer extends WifiPeer { return serviceInfo.getRepoAddress(); } + /** + * Return the fingerprint of the signing key, or {@code null} if it is not set. + */ @Override public String getFingerprint() { return serviceInfo.getFingerprint(); diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/type/BluetoothSwap.java b/F-Droid/src/org/fdroid/fdroid/localrepo/type/BluetoothSwap.java index 57563250a..7894ebeb9 100644 --- a/F-Droid/src/org/fdroid/fdroid/localrepo/type/BluetoothSwap.java +++ b/F-Droid/src/org/fdroid/fdroid/localrepo/type/BluetoothSwap.java @@ -14,7 +14,7 @@ import org.fdroid.fdroid.net.bluetooth.BluetoothServer; public class BluetoothSwap extends SwapType { - private static final String TAG = "BluetoothBroadcastType"; + private static final String TAG = "BluetoothSwap"; public final static String BLUETOOTH_NAME_TAG = "FDroid:"; private static BluetoothSwap mInstance = null; diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/type/BonjourBroadcast.java b/F-Droid/src/org/fdroid/fdroid/localrepo/type/BonjourBroadcast.java index bd29ea26a..3d6743858 100644 --- a/F-Droid/src/org/fdroid/fdroid/localrepo/type/BonjourBroadcast.java +++ b/F-Droid/src/org/fdroid/fdroid/localrepo/type/BonjourBroadcast.java @@ -20,7 +20,7 @@ import javax.jmdns.ServiceInfo; */ public class BonjourBroadcast extends SwapType { - private static final String TAG = "BonjourSwapService"; + private static final String TAG = "BonjourBroadcast"; private JmDNS jmdns; private ServiceInfo pairService; diff --git a/F-Droid/src/org/fdroid/fdroid/localrepo/type/WifiSwap.java b/F-Droid/src/org/fdroid/fdroid/localrepo/type/WifiSwap.java index 2d6bd629e..33ef06172 100644 --- a/F-Droid/src/org/fdroid/fdroid/localrepo/type/WifiSwap.java +++ b/F-Droid/src/org/fdroid/fdroid/localrepo/type/WifiSwap.java @@ -21,7 +21,7 @@ import java.util.Random; public class WifiSwap extends SwapType { - private static final String TAG = "WebServerType"; + private static final String TAG = "WifiSwap"; private Handler webServerThreadHandler = null; private LocalHTTPD localHttpd; diff --git a/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java b/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java index 860371ec8..5befdbaf2 100644 --- a/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java +++ b/F-Droid/src/org/fdroid/fdroid/net/WifiStateChangeService.java @@ -207,7 +207,8 @@ public class WifiStateChangeService extends Service { // the following methods were not added until android-9/Gingerbread for (InterfaceAddress address : netIf.getInterfaceAddresses()) { if (inetAddress.equals(address.getAddress()) && !TextUtils.isEmpty(FDroidApp.ipAddressString)) { - String cidr = String.format("%s/%d", FDroidApp.ipAddressString, address.getNetworkPrefixLength()); + String cidr = String.format(Locale.ENGLISH, "%s/%d", + FDroidApp.ipAddressString, address.getNetworkPrefixLength()); FDroidApp.subnetInfo = (new SubnetUtils(cidr)).getInfo(); break; } diff --git a/F-Droid/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java b/F-Droid/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java index 8b907f510..8ce67a395 100644 --- a/F-Droid/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java +++ b/F-Droid/src/org/fdroid/fdroid/views/fragments/AvailableAppsFragment.java @@ -84,7 +84,11 @@ public class AvailableAppsFragment extends AppListFragment implements // Wanted to just do this update here, but android tells // me that "Only the original thread that created a view // hierarchy can touch its views." - getActivity().runOnUiThread(new Runnable() { + final Activity activity = getActivity(); + // this nullguard is temporary, this Fragment really needs to merged into the Activity + if (activity == null) + return; + activity.runOnUiThread(new Runnable() { @Override public void run() { if (adapter == null) {