Merge remote-tracking branch 'origin/fix-external-provider'

This commit is contained in:
Dominik Schürmann 2018-06-11 10:00:34 +02:00
commit a2b2cadcd1
3 changed files with 62 additions and 83 deletions

View file

@ -55,7 +55,7 @@ import timber.log.Timber;
import static android.database.DatabaseUtils.dumpCursorToString;
public class KeychainProvider extends ContentProvider {
public class KeychainProvider extends ContentProvider implements SimpleContentResolverInterface {
private static final int KEY_RINGS_UNIFIED = 101;
private static final int KEY_RINGS_PUBLIC = 102;

View file

@ -25,14 +25,12 @@ import java.util.List;
import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.os.Binder;
import android.support.annotation.NonNull;
import android.text.TextUtils;
@ -42,7 +40,6 @@ import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptRecommendationResult;
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptState;
import org.sufficientlysecure.keychain.provider.KeychainContract;
import org.sufficientlysecure.keychain.provider.KeychainContract.ApiApps;
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
@ -68,14 +65,13 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
private static final int API_APPS = 301;
private static final int API_APPS_BY_PACKAGE_NAME = 302;
private static final int AUTOCRYPT_PEERS_BY_PACKAGE_NAME = 602;
public static final String TEMP_TABLE_QUERIED_ADDRESSES = "queried_addresses";
public static final String TEMP_TABLE_COLUMN_ADDRES = "address";
private UriMatcher mUriMatcher;
private ApiPermissionHelper mApiPermissionHelper;
private UriMatcher uriMatcher;
private ApiPermissionHelper apiPermissionHelper;
private KeychainProvider internalKeychainProvider;
/**
@ -99,20 +95,17 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
matcher.addURI(authority, KeychainExternalContract.BASE_AUTOCRYPT_STATUS, AUTOCRYPT_STATUS);
matcher.addURI(authority, KeychainExternalContract.BASE_AUTOCRYPT_STATUS + "/*", AUTOCRYPT_STATUS_INTERNAL);
// can only query status of calling app - for internal use only!
matcher.addURI(KeychainContract.CONTENT_AUTHORITY, KeychainContract.BASE_API_APPS + "/*", API_APPS_BY_PACKAGE_NAME);
matcher.addURI(KeychainContract.CONTENT_AUTHORITY, KeychainContract.BASE_AUTOCRYPT_PEERS + "/" +
KeychainContract.PATH_BY_PACKAGE_NAME + "/*", AUTOCRYPT_PEERS_BY_PACKAGE_NAME);
return matcher;
}
/** {@inheritDoc} */
@Override
public boolean onCreate() {
mUriMatcher = buildUriMatcher();
mApiPermissionHelper = new ApiPermissionHelper(getContext(), new ApiDataAccessObject(this));
uriMatcher = buildUriMatcher();
internalKeychainProvider = new KeychainProvider();
internalKeychainProvider.attachInfo(getContext(), null);
apiPermissionHelper = new ApiPermissionHelper(getContext(), new ApiDataAccessObject(internalKeychainProvider));
return true;
}
@ -121,7 +114,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
*/
@Override
public String getType(@NonNull Uri uri) {
final int match = mUriMatcher.match(uri);
final int match = uriMatcher.match(uri);
switch (match) {
case EMAIL_STATUS:
return EmailStatus.CONTENT_TYPE;
@ -148,17 +141,17 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
int match = mUriMatcher.match(uri);
int match = uriMatcher.match(uri);
String groupBy = null;
SQLiteDatabase db = new KeychainDatabase(getContext()).getReadableDatabase();
String callingPackageName = mApiPermissionHelper.getCurrentCallingPackage();
String callingPackageName = apiPermissionHelper.getCurrentCallingPackage();
switch (match) {
case EMAIL_STATUS: {
boolean callerIsAllowed = mApiPermissionHelper.isAllowedIgnoreErrors();
boolean callerIsAllowed = apiPermissionHelper.isAllowedIgnoreErrors();
if (!callerIsAllowed) {
throw new AccessControlException("An application must register before use of KeychainExternalProvider!");
}
@ -183,7 +176,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
+ " WHEN " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED
+ " WHEN NULL THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED
+ " END AS " + EmailStatus.USER_ID_STATUS);
projectionMap.put(EmailStatus.USER_ID, Tables.USER_PACKETS + "." + UserPackets.USER_ID + " AS " + EmailStatus.USER_ID);
projectionMap.put(EmailStatus.MASTER_KEY_ID,
Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " AS " + EmailStatus.MASTER_KEY_ID);
qb.setProjectionMap(projectionMap);
@ -233,7 +225,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
callingPackageName = uri.getLastPathSegment();
case AUTOCRYPT_STATUS: {
boolean callerIsAllowed = (match == AUTOCRYPT_STATUS_INTERNAL) || mApiPermissionHelper.isAllowedIgnoreErrors();
boolean callerIsAllowed = (match == AUTOCRYPT_STATUS_INTERNAL) || apiPermissionHelper.isAllowedIgnoreErrors();
if (!callerIsAllowed) {
throw new AccessControlException("An application must register before use of KeychainExternalProvider!");
}
@ -278,10 +270,21 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
}
if (!isWildcardSelector && queriesAutocryptResult) {
AutocryptPeerDataAccessObject autocryptPeerDao =
new AutocryptPeerDataAccessObject(this, callingPackageName);
new AutocryptPeerDataAccessObject(internalKeychainProvider, callingPackageName);
fillTempTableWithAutocryptRecommendations(db, autocryptPeerDao, selectionArgs);
}
HashMap<String, String> projectionMap = new HashMap<>();
projectionMap.put(AutocryptStatus._ID, AutocryptStatus._ID);
projectionMap.put(AutocryptStatus.ADDRESS, AutocryptStatus.ADDRESS);
projectionMap.put(AutocryptStatus.UID_KEY_STATUS, AutocryptStatus.UID_KEY_STATUS);
projectionMap.put(AutocryptStatus.UID_ADDRESS, AutocryptStatus.UID_ADDRESS);
projectionMap.put(AutocryptStatus.UID_MASTER_KEY_ID, AutocryptStatus.UID_MASTER_KEY_ID);
projectionMap.put(AutocryptStatus.UID_CANDIDATES, AutocryptStatus.UID_CANDIDATES);
projectionMap.put(AutocryptStatus.AUTOCRYPT_PEER_STATE, AutocryptStatus.AUTOCRYPT_PEER_STATE);
projectionMap.put(AutocryptStatus.AUTOCRYPT_KEY_STATUS, AutocryptStatus.AUTOCRYPT_KEY_STATUS);
projectionMap.put(AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID, AutocryptStatus.AUTOCRYPT_MASTER_KEY_ID);
qb.setProjectionMap(projectionMap);
qb.setTables(TEMP_TABLE_QUERIED_ADDRESSES);
if (TextUtils.isEmpty(sortOrder)) {
@ -293,22 +296,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
break;
}
case API_APPS_BY_PACKAGE_NAME: {
String requestedPackageName = uri.getLastPathSegment();
checkIfPackageBelongsToCaller(getContext(), requestedPackageName);
qb.setTables(Tables.API_APPS);
qb.appendWhere(ApiApps.PACKAGE_NAME + " = ");
qb.appendWhereEscapeString(requestedPackageName);
break;
}
case AUTOCRYPT_PEERS_BY_PACKAGE_NAME:
KeychainProvider keychainProvider = new KeychainProvider();
keychainProvider.attachInfo(getContext(), null);
return keychainProvider.query(uri, projection, selection, selectionArgs, sortOrder);
default: {
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
}
@ -323,7 +310,8 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
orderBy = sortOrder;
}
Cursor cursor = qb.query(db, projection, selection, null, groupBy, null, orderBy);
qb.setStrict(true);
Cursor cursor = qb.query(db, projection, null, null, groupBy, null, orderBy);
qb.setCursorFactory(new CloseDatabaseCursorFactory());
if (cursor != null) {
// Tell the cursor what uri to watch, so it knows when its source data changes
@ -406,30 +394,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
+ " GROUP BY " + TEMP_TABLE_QUERIED_ADDRESSES + "." + TEMP_TABLE_COLUMN_ADDRES);
}
private void explainQuery(SQLiteDatabase db, String sql) {
Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + sql, new String[0]);
// this is a debugging feature, we can be a little careless
explainCursor.moveToFirst();
StringBuilder line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getColumnName(i)).append(", ");
}
Timber.d(Constants.TAG, line.toString());
while (!explainCursor.isAfterLast()) {
line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getString(i)).append(", ");
}
Timber.d(Constants.TAG, line.toString());
explainCursor.moveToNext();
}
explainCursor.close();
}
private int getPeerStateValue(AutocryptState autocryptState) {
switch (autocryptState) {
case DISABLE: return AutocryptStatus.AUTOCRYPT_PEER_DISABLED;
@ -441,25 +405,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
throw new IllegalStateException("Unhandled case!");
}
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
public Uri insert(@NonNull Uri uri, ContentValues values) {
throw new UnsupportedOperationException();

View file

@ -0,0 +1,34 @@
package org.sufficientlysecure.keychain.util;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import timber.log.Timber;
public class DatabaseUtils {
public static void explainQuery(SQLiteDatabase db, String sql) {
Cursor explainCursor = db.rawQuery("EXPLAIN QUERY PLAN " + sql, new String[0]);
// this is a debugging feature, we can be a little careless
explainCursor.moveToFirst();
StringBuilder line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getColumnName(i)).append(", ");
}
Timber.d(line.toString());
while (!explainCursor.isAfterLast()) {
line = new StringBuilder();
for (int i = 0; i < explainCursor.getColumnCount(); i++) {
line.append(explainCursor.getString(i)).append(", ");
}
Timber.d(line.toString());
explainCursor.moveToNext();
}
explainCursor.close();
}
}