Merge branch 'small-swap-fixes' into 'master'

Small swap fixes

These are a few fixes I found while debugging the new swap stuff.  The `repo.fingerprint` commit is quite important since that is the code that provides the core of FDroid's security model.

See merge request !142
This commit is contained in:
Hans-Christoph Steiner 2015-09-16 17:53:42 +00:00
commit 4fb173e364
12 changed files with 67 additions and 24 deletions

View File

@ -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() {

View File

@ -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.
* <p/>
* <b>WARNING</b>: 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 + "'");
}
}

View File

@ -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;

View File

@ -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));

View File

@ -259,6 +259,8 @@ public class SwapService extends Service {
values.put(RepoProvider.DataColumns.NAME, peer.getName());
values.put(RepoProvider.DataColumns.ADDRESS, peer.getRepoAddress());
values.put(RepoProvider.DataColumns.DESCRIPTION, "");
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);

View File

@ -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.
* <p/>
* 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

View File

@ -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();

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;
}

View File

@ -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) {