Verify permissions of downloaded apk
This commit is contained in:
parent
1652c32d51
commit
4bed9d67c5
@ -5,12 +5,12 @@ import android.content.ContentValues;
|
|||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Parcelable;
|
import android.os.Parcelable;
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import org.fdroid.fdroid.Utils;
|
import org.fdroid.fdroid.Utils;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
public class Apk extends ValueObject implements Comparable<Apk> {
|
public class Apk extends ValueObject implements Comparable<Apk> {
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ public class Apk extends ValueObject implements Comparable<Apk> {
|
|||||||
|
|
||||||
public ArrayList<String> getFullPermissionList() {
|
public ArrayList<String> getFullPermissionList() {
|
||||||
if (this.permissions == null) {
|
if (this.permissions == null) {
|
||||||
return null;
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<String> permissionsFull = new ArrayList<>();
|
ArrayList<String> permissionsFull = new ArrayList<>();
|
||||||
@ -157,13 +157,13 @@ public class Apk extends ValueObject implements Comparable<Apk> {
|
|||||||
|
|
||||||
public String[] getFullPermissionsArray() {
|
public String[] getFullPermissionsArray() {
|
||||||
ArrayList<String> fullPermissions = getFullPermissionList();
|
ArrayList<String> fullPermissions = getFullPermissionList();
|
||||||
if (fullPermissions == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return fullPermissions.toArray(new String[fullPermissions.size()]);
|
return fullPermissions.toArray(new String[fullPermissions.size()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public HashSet<String> getFullPermissionsSet() {
|
||||||
|
return new HashSet<>(getFullPermissionList());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* It appears that the default Android permissions in android.Manifest.permissions
|
* It appears that the default Android permissions in android.Manifest.permissions
|
||||||
* are prefixed with "android.permission." and then the constant name.
|
* are prefixed with "android.permission." and then the constant name.
|
||||||
|
@ -23,31 +23,37 @@ import android.content.Context;
|
|||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import org.apache.commons.io.FileUtils;
|
import org.apache.commons.io.FileUtils;
|
||||||
import org.fdroid.fdroid.Hasher;
|
import org.fdroid.fdroid.Hasher;
|
||||||
|
import org.fdroid.fdroid.Utils;
|
||||||
import org.fdroid.fdroid.data.Apk;
|
import org.fdroid.fdroid.data.Apk;
|
||||||
import org.fdroid.fdroid.data.SanitizedFile;
|
import org.fdroid.fdroid.data.SanitizedFile;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
public class ApkVerifier {
|
public class ApkVerifier {
|
||||||
|
|
||||||
|
private static final String TAG = "ApkVerifier";
|
||||||
|
|
||||||
Context context;
|
Context context;
|
||||||
Uri localApkUri;
|
Uri localApkUri;
|
||||||
Apk apk;
|
Apk expectedApk;
|
||||||
PackageManager pm;
|
PackageManager pm;
|
||||||
|
|
||||||
ApkVerifier(Context context, Uri localApkUri, Apk apk) {
|
ApkVerifier(Context context, Uri localApkUri, Apk expectedApk) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.localApkUri = localApkUri;
|
this.localApkUri = localApkUri;
|
||||||
this.apk = apk;
|
this.expectedApk = expectedApk;
|
||||||
this.pm = context.getPackageManager();
|
this.pm = context.getPackageManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void basicVerify() throws ApkVerificationException {
|
public void verifyApk() throws ApkVerificationException {
|
||||||
PackageInfo localApkInfo = pm.getPackageArchiveInfo(
|
PackageInfo localApkInfo = pm.getPackageArchiveInfo(
|
||||||
localApkUri.getPath(), PackageManager.GET_PERMISSIONS);
|
localApkUri.getPath(), PackageManager.GET_PERMISSIONS);
|
||||||
if (localApkInfo == null) {
|
if (localApkInfo == null) {
|
||||||
@ -55,13 +61,33 @@ public class ApkVerifier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check if the apk has the expected packageName
|
// 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!");
|
throw new ApkVerificationException("apk has unexpected packageName!");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localApkInfo.versionCode < 0) {
|
if (localApkInfo.versionCode < 0) {
|
||||||
throw new ApkVerificationException("apk has no valid versionCode!");
|
throw new ApkVerificationException("apk has no valid versionCode!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// verify permissions, important for unattended installer
|
||||||
|
HashSet<String> localPermissions = getLocalPermissionsSet(localApkInfo);
|
||||||
|
HashSet<String> 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<String> 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 {
|
public Uri getSafeUri() throws ApkVerificationException {
|
||||||
@ -77,7 +103,7 @@ public class ApkVerifier {
|
|||||||
sanitizedApkFile = SanitizedFile.knownSanitized(File.createTempFile("install-", ".apk",
|
sanitizedApkFile = SanitizedFile.knownSanitized(File.createTempFile("install-", ".apk",
|
||||||
context.getFilesDir()));
|
context.getFilesDir()));
|
||||||
FileUtils.copyFile(apkFile, sanitizedApkFile);
|
FileUtils.copyFile(apkFile, sanitizedApkFile);
|
||||||
if (!verifyApkFile(sanitizedApkFile, apk.hash, apk.hashType)) {
|
if (!verifyApkFile(sanitizedApkFile, expectedApk.hash, expectedApk.hashType)) {
|
||||||
FileUtils.deleteQuietly(apkFile);
|
FileUtils.deleteQuietly(apkFile);
|
||||||
throw new ApkVerificationException(apkFile + " failed to verify!");
|
throw new ApkVerificationException(apkFile + " failed to verify!");
|
||||||
}
|
}
|
||||||
|
@ -241,7 +241,7 @@ public class InstallManagerService extends Service {
|
|||||||
Uri sanitizedUri;
|
Uri sanitizedUri;
|
||||||
try {
|
try {
|
||||||
ApkVerifier apkVerifier = new ApkVerifier(context, localApkUri, apk);
|
ApkVerifier apkVerifier = new ApkVerifier(context, localApkUri, apk);
|
||||||
apkVerifier.basicVerify();
|
apkVerifier.verifyApk();
|
||||||
sanitizedUri = apkVerifier.getSafeUri();
|
sanitizedUri = apkVerifier.getSafeUri();
|
||||||
} catch (ApkVerifier.ApkVerificationException e) {
|
} catch (ApkVerifier.ApkVerificationException e) {
|
||||||
Log.e(TAG, "ApkVerifier failed", e);
|
Log.e(TAG, "ApkVerifier failed", e);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user