external-provider: only allow permission check for caller package names
This commit is contained in:
@@ -18,6 +18,10 @@
|
|||||||
package org.sufficientlysecure.keychain.remote;
|
package org.sufficientlysecure.keychain.remote;
|
||||||
|
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.PendingIntent;
|
import android.app.PendingIntent;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -37,11 +41,6 @@ import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
|
|||||||
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
import org.sufficientlysecure.keychain.provider.KeychainContract;
|
||||||
import org.sufficientlysecure.keychain.util.Log;
|
import org.sufficientlysecure.keychain.util.Log;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Abstract service class for remote APIs that handle app registration and user input.
|
* Abstract service class for remote APIs that handle app registration and user input.
|
||||||
@@ -234,35 +233,29 @@ public class ApiPermissionHelper {
|
|||||||
private boolean isPackageAllowed(String packageName) throws WrongPackageCertificateException {
|
private boolean isPackageAllowed(String packageName) throws WrongPackageCertificateException {
|
||||||
Log.d(Constants.TAG, "isPackageAllowed packageName: " + packageName);
|
Log.d(Constants.TAG, "isPackageAllowed packageName: " + packageName);
|
||||||
|
|
||||||
ArrayList<String> allowedPkgs = mApiDao.getRegisteredApiApps();
|
byte[] storedPackageCert = mApiDao.getApiAppCertificate(packageName);
|
||||||
Log.d(Constants.TAG, "allowed: " + allowedPkgs);
|
|
||||||
|
|
||||||
// check if package is allowed to use our service
|
boolean isKnownPackage = storedPackageCert != null;
|
||||||
if (allowedPkgs.contains(packageName)) {
|
if (!isKnownPackage) {
|
||||||
Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName);
|
Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Log.d(Constants.TAG, "Package is allowed! packageName: " + packageName);
|
||||||
|
|
||||||
// check package signature
|
byte[] currentPackageCert;
|
||||||
byte[] currentCert;
|
try {
|
||||||
try {
|
currentPackageCert = getPackageCertificate(packageName);
|
||||||
currentCert = getPackageCertificate(packageName);
|
} catch (NameNotFoundException e) {
|
||||||
} catch (NameNotFoundException e) {
|
throw new WrongPackageCertificateException(e.getMessage());
|
||||||
throw new WrongPackageCertificateException(e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] storedCert = mApiDao.getApiAppCertificate(packageName);
|
|
||||||
if (Arrays.equals(currentCert, storedCert)) {
|
|
||||||
Log.d(Constants.TAG,
|
|
||||||
"Package certificate is correct! (equals certificate from database)");
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
throw new WrongPackageCertificateException(
|
|
||||||
"PACKAGE NOT ALLOWED! Certificate wrong! (Certificate not " +
|
|
||||||
"equals certificate from database)");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(Constants.TAG, "Package is NOT allowed! packageName: " + packageName);
|
boolean packageCertMatchesStored = Arrays.equals(currentPackageCert, storedPackageCert);
|
||||||
return false;
|
if (packageCertMatchesStored) {
|
||||||
|
Log.d(Constants.TAG,"Package certificate matches expected.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new WrongPackageCertificateException("PACKAGE NOT ALLOWED DUE TO CERTIFICATE MISMATCH!");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,12 +24,14 @@ import java.util.HashMap;
|
|||||||
|
|
||||||
import android.content.ContentProvider;
|
import android.content.ContentProvider;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
import android.content.UriMatcher;
|
import android.content.UriMatcher;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.database.DatabaseUtils;
|
import android.database.DatabaseUtils;
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteQueryBuilder;
|
import android.database.sqlite.SQLiteQueryBuilder;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Binder;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
@@ -206,16 +208,13 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case API_APPS: {
|
|
||||||
qb.setTables(Tables.API_APPS);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case API_APPS_BY_PACKAGE_NAME: {
|
case API_APPS_BY_PACKAGE_NAME: {
|
||||||
|
String requestedPackageName = uri.getLastPathSegment();
|
||||||
|
checkIfPackageBelongsToCaller(getContext(), requestedPackageName);
|
||||||
|
|
||||||
qb.setTables(Tables.API_APPS);
|
qb.setTables(Tables.API_APPS);
|
||||||
qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
|
qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
|
||||||
qb.appendWhereEscapeString(uri.getLastPathSegment());
|
qb.appendWhereEscapeString(requestedPackageName);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -248,6 +247,25 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
|||||||
return cursor;
|
return cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkIfPackageBelongsToCaller(Context context, String requestedPackageName) {
|
||||||
|
int callerUid = Binder.getCallingUid();
|
||||||
|
String[] callerPackageNames = context.getPackageManager().getPackagesForUid(callerUid);
|
||||||
|
if (callerPackageNames == null) {
|
||||||
|
throw new IllegalStateException("Failed to retrieve caller package name, this is an error!");
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean packageBelongsToCaller = false;
|
||||||
|
for (String p : callerPackageNames) {
|
||||||
|
if (p.equals(requestedPackageName)) {
|
||||||
|
packageBelongsToCaller = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!packageBelongsToCaller) {
|
||||||
|
throw new SecurityException("ExternalProvider may only check status of caller package!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|||||||
Reference in New Issue
Block a user