diff --git a/app/src/main/java/org/fdroid/fdroid/data/Apk.java b/app/src/main/java/org/fdroid/fdroid/data/Apk.java index 5d90b4e2a..81d84d6ce 100644 --- a/app/src/main/java/org/fdroid/fdroid/data/Apk.java +++ b/app/src/main/java/org/fdroid/fdroid/data/Apk.java @@ -5,12 +5,12 @@ import android.content.ContentValues; import android.database.Cursor; import android.os.Build; import android.os.Parcelable; -import android.util.Log; import org.fdroid.fdroid.Utils; import java.util.ArrayList; import java.util.Date; +import java.util.HashSet; public class Apk extends ValueObject implements Comparable { @@ -145,7 +145,7 @@ public class Apk extends ValueObject implements Comparable { public ArrayList getFullPermissionList() { if (this.permissions == null) { - return null; + return new ArrayList<>(); } ArrayList permissionsFull = new ArrayList<>(); @@ -157,13 +157,13 @@ public class Apk extends ValueObject implements Comparable { public String[] getFullPermissionsArray() { ArrayList fullPermissions = getFullPermissionList(); - if (fullPermissions == null) { - return null; - } - return fullPermissions.toArray(new String[fullPermissions.size()]); } + public HashSet getFullPermissionsSet() { + return new HashSet<>(getFullPermissionList()); + } + /** * It appears that the default Android permissions in android.Manifest.permissions * are prefixed with "android.permission." and then the constant name. diff --git a/app/src/main/java/org/fdroid/fdroid/installer/ApkVerifier.java b/app/src/main/java/org/fdroid/fdroid/installer/ApkVerifier.java index 298631413..c818df9a5 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/ApkVerifier.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/ApkVerifier.java @@ -23,31 +23,37 @@ import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; +import android.util.Log; import org.apache.commons.io.FileUtils; import org.fdroid.fdroid.Hasher; +import org.fdroid.fdroid.Utils; import org.fdroid.fdroid.data.Apk; import org.fdroid.fdroid.data.SanitizedFile; import java.io.File; import java.io.IOException; import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.HashSet; public class ApkVerifier { + private static final String TAG = "ApkVerifier"; + Context context; Uri localApkUri; - Apk apk; + Apk expectedApk; PackageManager pm; - ApkVerifier(Context context, Uri localApkUri, Apk apk) { + ApkVerifier(Context context, Uri localApkUri, Apk expectedApk) { this.context = context; this.localApkUri = localApkUri; - this.apk = apk; + this.expectedApk = expectedApk; this.pm = context.getPackageManager(); } - public void basicVerify() throws ApkVerificationException { + public void verifyApk() throws ApkVerificationException { PackageInfo localApkInfo = pm.getPackageArchiveInfo( localApkUri.getPath(), PackageManager.GET_PERMISSIONS); if (localApkInfo == null) { @@ -55,13 +61,33 @@ public class ApkVerifier { } // check if the apk has the expected packageName - if (localApkInfo.packageName == null || !apk.packageName.equals(localApkInfo.packageName)) { + if (localApkInfo.packageName == null || !localApkInfo.packageName.equals(expectedApk.packageName)) { throw new ApkVerificationException("apk has unexpected packageName!"); } if (localApkInfo.versionCode < 0) { throw new ApkVerificationException("apk has no valid versionCode!"); } + + // verify permissions, important for unattended installer + HashSet localPermissions = getLocalPermissionsSet(localApkInfo); + HashSet expectedPermissions = expectedApk.getFullPermissionsSet(); + Utils.debugLog(TAG, "localPermissions: " + localPermissions); + Utils.debugLog(TAG, "expectedPermissions: " + expectedPermissions); + if (!localPermissions.equals(expectedPermissions)) { + throw new ApkVerificationException("permissions of apk not equals expected permissions!"); + } + + // TODO: check target sdk + } + + private HashSet getLocalPermissionsSet(PackageInfo localApkInfo) { + String[] localPermissions = localApkInfo.requestedPermissions; + if (localPermissions == null) { + return new HashSet<>(); + } + + return new HashSet<>(Arrays.asList(localApkInfo.requestedPermissions)); } public Uri getSafeUri() throws ApkVerificationException { @@ -77,7 +103,7 @@ public class ApkVerifier { sanitizedApkFile = SanitizedFile.knownSanitized(File.createTempFile("install-", ".apk", context.getFilesDir())); FileUtils.copyFile(apkFile, sanitizedApkFile); - if (!verifyApkFile(sanitizedApkFile, apk.hash, apk.hashType)) { + if (!verifyApkFile(sanitizedApkFile, expectedApk.hash, expectedApk.hashType)) { FileUtils.deleteQuietly(apkFile); throw new ApkVerificationException(apkFile + " failed to verify!"); } diff --git a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java index d80c4de84..1486b0215 100644 --- a/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java +++ b/app/src/main/java/org/fdroid/fdroid/installer/InstallManagerService.java @@ -241,7 +241,7 @@ public class InstallManagerService extends Service { Uri sanitizedUri; try { ApkVerifier apkVerifier = new ApkVerifier(context, localApkUri, apk); - apkVerifier.basicVerify(); + apkVerifier.verifyApk(); sanitizedUri = apkVerifier.getSafeUri(); } catch (ApkVerifier.ApkVerificationException e) { Log.e(TAG, "ApkVerifier failed", e);