Merge remote-tracking branch 'origin/fix-external-provider'
This commit is contained in:
commit
a2b2cadcd1
|
@ -55,7 +55,7 @@ import timber.log.Timber;
|
||||||
import static android.database.DatabaseUtils.dumpCursorToString;
|
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_UNIFIED = 101;
|
||||||
private static final int KEY_RINGS_PUBLIC = 102;
|
private static final int KEY_RINGS_PUBLIC = 102;
|
||||||
|
|
|
@ -25,14 +25,12 @@ import java.util.List;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
@ -42,7 +40,6 @@ import org.sufficientlysecure.keychain.provider.ApiDataAccessObject;
|
||||||
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject;
|
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject;
|
||||||
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptRecommendationResult;
|
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptRecommendationResult;
|
||||||
import org.sufficientlysecure.keychain.provider.AutocryptPeerDataAccessObject.AutocryptState;
|
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.ApiApps;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
|
import org.sufficientlysecure.keychain.provider.KeychainContract.Certs;
|
||||||
import org.sufficientlysecure.keychain.provider.KeychainContract.KeyRings;
|
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 = 301;
|
||||||
private static final int API_APPS_BY_PACKAGE_NAME = 302;
|
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_QUERIED_ADDRESSES = "queried_addresses";
|
||||||
public static final String TEMP_TABLE_COLUMN_ADDRES = "address";
|
public static final String TEMP_TABLE_COLUMN_ADDRES = "address";
|
||||||
|
|
||||||
|
|
||||||
private UriMatcher mUriMatcher;
|
private UriMatcher uriMatcher;
|
||||||
private ApiPermissionHelper mApiPermissionHelper;
|
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);
|
||||||
matcher.addURI(authority, KeychainExternalContract.BASE_AUTOCRYPT_STATUS + "/*", AUTOCRYPT_STATUS_INTERNAL);
|
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;
|
return matcher;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** {@inheritDoc} */
|
/** {@inheritDoc} */
|
||||||
@Override
|
@Override
|
||||||
public boolean onCreate() {
|
public boolean onCreate() {
|
||||||
mUriMatcher = buildUriMatcher();
|
uriMatcher = buildUriMatcher();
|
||||||
mApiPermissionHelper = new ApiPermissionHelper(getContext(), new ApiDataAccessObject(this));
|
|
||||||
|
internalKeychainProvider = new KeychainProvider();
|
||||||
|
internalKeychainProvider.attachInfo(getContext(), null);
|
||||||
|
apiPermissionHelper = new ApiPermissionHelper(getContext(), new ApiDataAccessObject(internalKeychainProvider));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,7 +114,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public String getType(@NonNull Uri uri) {
|
public String getType(@NonNull Uri uri) {
|
||||||
final int match = mUriMatcher.match(uri);
|
final int match = uriMatcher.match(uri);
|
||||||
switch (match) {
|
switch (match) {
|
||||||
case EMAIL_STATUS:
|
case EMAIL_STATUS:
|
||||||
return EmailStatus.CONTENT_TYPE;
|
return EmailStatus.CONTENT_TYPE;
|
||||||
|
@ -148,17 +141,17 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||||
|
|
||||||
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
|
||||||
|
|
||||||
int match = mUriMatcher.match(uri);
|
int match = uriMatcher.match(uri);
|
||||||
|
|
||||||
String groupBy = null;
|
String groupBy = null;
|
||||||
|
|
||||||
SQLiteDatabase db = new KeychainDatabase(getContext()).getReadableDatabase();
|
SQLiteDatabase db = new KeychainDatabase(getContext()).getReadableDatabase();
|
||||||
|
|
||||||
String callingPackageName = mApiPermissionHelper.getCurrentCallingPackage();
|
String callingPackageName = apiPermissionHelper.getCurrentCallingPackage();
|
||||||
|
|
||||||
switch (match) {
|
switch (match) {
|
||||||
case EMAIL_STATUS: {
|
case EMAIL_STATUS: {
|
||||||
boolean callerIsAllowed = mApiPermissionHelper.isAllowedIgnoreErrors();
|
boolean callerIsAllowed = apiPermissionHelper.isAllowedIgnoreErrors();
|
||||||
if (!callerIsAllowed) {
|
if (!callerIsAllowed) {
|
||||||
throw new AccessControlException("An application must register before use of KeychainExternalProvider!");
|
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 " + Certs.VERIFIED_SECRET + " THEN " + KeychainExternalContract.KEY_STATUS_VERIFIED
|
||||||
+ " WHEN NULL THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED
|
+ " WHEN NULL THEN " + KeychainExternalContract.KEY_STATUS_UNVERIFIED
|
||||||
+ " END AS " + EmailStatus.USER_ID_STATUS);
|
+ " 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,
|
projectionMap.put(EmailStatus.MASTER_KEY_ID,
|
||||||
Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " AS " + EmailStatus.MASTER_KEY_ID);
|
Tables.USER_PACKETS + "." + UserPackets.MASTER_KEY_ID + " AS " + EmailStatus.MASTER_KEY_ID);
|
||||||
qb.setProjectionMap(projectionMap);
|
qb.setProjectionMap(projectionMap);
|
||||||
|
@ -233,7 +225,7 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||||
callingPackageName = uri.getLastPathSegment();
|
callingPackageName = uri.getLastPathSegment();
|
||||||
|
|
||||||
case AUTOCRYPT_STATUS: {
|
case AUTOCRYPT_STATUS: {
|
||||||
boolean callerIsAllowed = (match == AUTOCRYPT_STATUS_INTERNAL) || mApiPermissionHelper.isAllowedIgnoreErrors();
|
boolean callerIsAllowed = (match == AUTOCRYPT_STATUS_INTERNAL) || apiPermissionHelper.isAllowedIgnoreErrors();
|
||||||
if (!callerIsAllowed) {
|
if (!callerIsAllowed) {
|
||||||
throw new AccessControlException("An application must register before use of KeychainExternalProvider!");
|
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) {
|
if (!isWildcardSelector && queriesAutocryptResult) {
|
||||||
AutocryptPeerDataAccessObject autocryptPeerDao =
|
AutocryptPeerDataAccessObject autocryptPeerDao =
|
||||||
new AutocryptPeerDataAccessObject(this, callingPackageName);
|
new AutocryptPeerDataAccessObject(internalKeychainProvider, callingPackageName);
|
||||||
fillTempTableWithAutocryptRecommendations(db, autocryptPeerDao, selectionArgs);
|
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);
|
qb.setTables(TEMP_TABLE_QUERIED_ADDRESSES);
|
||||||
|
|
||||||
if (TextUtils.isEmpty(sortOrder)) {
|
if (TextUtils.isEmpty(sortOrder)) {
|
||||||
|
@ -293,22 +296,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||||
break;
|
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: {
|
default: {
|
||||||
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
|
throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")");
|
||||||
}
|
}
|
||||||
|
@ -323,7 +310,8 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||||
orderBy = sortOrder;
|
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());
|
qb.setCursorFactory(new CloseDatabaseCursorFactory());
|
||||||
if (cursor != null) {
|
if (cursor != null) {
|
||||||
// Tell the cursor what uri to watch, so it knows when its source data changes
|
// 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);
|
+ " 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) {
|
private int getPeerStateValue(AutocryptState autocryptState) {
|
||||||
switch (autocryptState) {
|
switch (autocryptState) {
|
||||||
case DISABLE: return AutocryptStatus.AUTOCRYPT_PEER_DISABLED;
|
case DISABLE: return AutocryptStatus.AUTOCRYPT_PEER_DISABLED;
|
||||||
|
@ -441,25 +405,6 @@ public class KeychainExternalProvider extends ContentProvider implements SimpleC
|
||||||
throw new IllegalStateException("Unhandled case!");
|
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
|
@Override
|
||||||
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
public Uri insert(@NonNull Uri uri, ContentValues values) {
|
||||||
throw new UnsupportedOperationException();
|
throw new UnsupportedOperationException();
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue