diff options
author | Adithya Abraham Philip <adithyaphilip@gmail.com> | 2015-06-30 22:12:16 +0530 |
---|---|---|
committer | Adithya Abraham Philip <adithyaphilip@gmail.com> | 2015-06-30 22:12:16 +0530 |
commit | a5257ec71d41532ac62594f59c10ba47d6a9ca38 (patch) | |
tree | 6771ba3e13df96574e093668c77e34678511b5f7 /OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider | |
parent | 51d3daeccdc697d948c597931d804d738132b8ed (diff) | |
parent | 677afa90fce133b81c195488e07f07a5a83e2f0b (diff) | |
download | open-keychain-a5257ec71d41532ac62594f59c10ba47d6a9ca38.tar.gz open-keychain-a5257ec71d41532ac62594f59c10ba47d6a9ca38.tar.bz2 open-keychain-a5257ec71d41532ac62594f59c10ba47d6a9ca38.zip |
Merge branch 'master' of github.com:open-keychain/open-keychain
Diffstat (limited to 'OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider')
2 files changed, 90 insertions, 46 deletions
diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java index 1351e0cbc..3ed9b1da9 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/KeychainProvider.java @@ -520,7 +520,6 @@ public class KeychainProvider extends ContentProvider { } break; - } case KEY_RINGS_PUBLIC: @@ -607,23 +606,26 @@ public class KeychainProvider extends ContentProvider { break; } - case API_APPS: + case API_APPS: { qb.setTables(Tables.API_APPS); break; - case API_APPS_BY_PACKAGE_NAME: + } + case API_APPS_BY_PACKAGE_NAME: { qb.setTables(Tables.API_APPS); qb.appendWhere(ApiApps.PACKAGE_NAME + " = "); qb.appendWhereEscapeString(uri.getLastPathSegment()); break; - case API_ACCOUNTS: + } + case API_ACCOUNTS: { qb.setTables(Tables.API_ACCOUNTS); qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); break; - case API_ACCOUNTS_BY_ACCOUNT_NAME: + } + case API_ACCOUNTS_BY_ACCOUNT_NAME: { qb.setTables(Tables.API_ACCOUNTS); qb.appendWhere(Tables.API_ACCOUNTS + "." + ApiAccounts.PACKAGE_NAME + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); @@ -632,14 +634,17 @@ public class KeychainProvider extends ContentProvider { qb.appendWhereEscapeString(uri.getLastPathSegment()); break; - case API_ALLOWED_KEYS: + } + case API_ALLOWED_KEYS: { qb.setTables(Tables.API_ALLOWED_KEYS); qb.appendWhere(Tables.API_ALLOWED_KEYS + "." + ApiAccounts.PACKAGE_NAME + " = "); qb.appendWhereEscapeString(uri.getPathSegments().get(1)); break; - default: + } + default: { throw new IllegalArgumentException("Unknown URI " + uri + " (" + match + ")"); + } } @@ -684,47 +689,47 @@ public class KeychainProvider extends ContentProvider { final int match = mUriMatcher.match(uri); switch (match) { - case KEY_RING_PUBLIC: + case KEY_RING_PUBLIC: { db.insertOrThrow(Tables.KEY_RINGS_PUBLIC, null, values); keyId = values.getAsLong(KeyRings.MASTER_KEY_ID); break; - - case KEY_RING_SECRET: + } + case KEY_RING_SECRET: { db.insertOrThrow(Tables.KEY_RINGS_SECRET, null, values); keyId = values.getAsLong(KeyRings.MASTER_KEY_ID); break; - - case KEY_RING_KEYS: + } + case KEY_RING_KEYS: { db.insertOrThrow(Tables.KEYS, null, values); keyId = values.getAsLong(Keys.MASTER_KEY_ID); break; - - case KEY_RING_USER_IDS: + } + case KEY_RING_USER_IDS: { // iff TYPE is null, user_id MUST be null as well - if ( ! (values.get(UserPacketsColumns.TYPE) == null + if (!(values.get(UserPacketsColumns.TYPE) == null ? (values.get(UserPacketsColumns.USER_ID) != null && values.get(UserPacketsColumns.ATTRIBUTE_DATA) == null) : (values.get(UserPacketsColumns.ATTRIBUTE_DATA) != null && values.get(UserPacketsColumns.USER_ID) == null) - )) { + )) { throw new AssertionError("Incorrect type for user packet! This is a bug!"); } - if (((Number)values.get(UserPacketsColumns.RANK)).intValue() == 0 && values.get(UserPacketsColumns.USER_ID) == null) { + if (((Number) values.get(UserPacketsColumns.RANK)).intValue() == 0 && values.get(UserPacketsColumns.USER_ID) == null) { throw new AssertionError("Rank 0 user packet must be a user id!"); } db.insertOrThrow(Tables.USER_PACKETS, null, values); keyId = values.getAsLong(UserPackets.MASTER_KEY_ID); break; - - case KEY_RING_CERTS: + } + case KEY_RING_CERTS: { // we replace here, keeping only the latest signature // TODO this would be better handled in savePublicKeyRing directly! db.replaceOrThrow(Tables.CERTS, null, values); keyId = values.getAsLong(Certs.MASTER_KEY_ID); break; - - case API_APPS: + } + case API_APPS: { db.insertOrThrow(Tables.API_APPS, null, values); break; - + } case API_ACCOUNTS: { // set foreign key automatically based on given uri // e.g., api_apps/com.example.app/accounts/ @@ -743,8 +748,9 @@ public class KeychainProvider extends ContentProvider { db.insertOrThrow(Tables.API_ALLOWED_KEYS, null, values); break; } - default: + default: { throw new UnsupportedOperationException("Unknown uri: " + uri); + } } if (keyId != null) { @@ -802,20 +808,24 @@ public class KeychainProvider extends ContentProvider { break; } - case API_APPS_BY_PACKAGE_NAME: + case API_APPS_BY_PACKAGE_NAME: { count = db.delete(Tables.API_APPS, buildDefaultApiAppsSelection(uri, additionalSelection), selectionArgs); break; - case API_ACCOUNTS_BY_ACCOUNT_NAME: + } + case API_ACCOUNTS_BY_ACCOUNT_NAME: { count = db.delete(Tables.API_ACCOUNTS, buildDefaultApiAccountsSelection(uri, additionalSelection), selectionArgs); break; - case API_ALLOWED_KEYS: + } + case API_ALLOWED_KEYS: { count = db.delete(Tables.API_ALLOWED_KEYS, buildDefaultApiAllowedKeysSelection(uri, additionalSelection), selectionArgs); break; - default: + } + default: { throw new UnsupportedOperationException("Unknown uri: " + uri); + } } // notify of changes in db @@ -851,16 +861,19 @@ public class KeychainProvider extends ContentProvider { count = db.update(Tables.KEYS, values, actualSelection, selectionArgs); break; } - case API_APPS_BY_PACKAGE_NAME: + case API_APPS_BY_PACKAGE_NAME: { count = db.update(Tables.API_APPS, values, buildDefaultApiAppsSelection(uri, selection), selectionArgs); break; - case API_ACCOUNTS_BY_ACCOUNT_NAME: + } + case API_ACCOUNTS_BY_ACCOUNT_NAME: { count = db.update(Tables.API_ACCOUNTS, values, buildDefaultApiAccountsSelection(uri, selection), selectionArgs); break; - default: + } + default: { throw new UnsupportedOperationException("Unknown uri: " + uri); + } } // notify of changes in db diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java index b1f1a7d9e..7e9b24989 100644 --- a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/provider/TemporaryStorageProvider.java @@ -19,23 +19,15 @@ package org.sufficientlysecure.keychain.provider; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.util.UUID; - import android.content.ClipDescription; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; -import android.content.res.AssetFileDescriptor; import android.database.Cursor; import android.database.MatrixCursor; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.net.Uri; -import android.os.Bundle; -import android.os.CancellationSignal; import android.os.ParcelFileDescriptor; import android.provider.OpenableColumns; @@ -43,6 +35,30 @@ import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.util.DatabaseUtil; import org.sufficientlysecure.keychain.util.Log; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.util.UUID; + +/** + * TemporaryStorageProvider stores decrypted files inside the app's cache directory previously to + * sharing them with other applications. + * + * Security: + * - It is writable by OpenKeychain only (see Manifest), but exported for reading files + * - It uses UUIDs as identifiers which makes predicting files from outside impossible + * - Querying a number of files is not allowed, only querying single files + * -> You can only open a file if you know the Uri containing the precise UUID, this Uri is only + * revealed when the user shares a decrypted file with another app. + * + * Why is support lib's FileProvider not used? + * Because granting Uri permissions temporarily does not work correctly. See + * - https://code.google.com/p/android/issues/detail?id=76683 + * - https://github.com/nmr8acme/FileProvider-permission-bug + * - http://stackoverflow.com/q/24467696 + * - http://stackoverflow.com/q/18249007 + * - Comments at http://www.blogc.at/2014/03/23/share-private-files-with-other-apps-fileprovider/ + */ public class TemporaryStorageProvider extends ContentProvider { private static final String DB_NAME = "tempstorage.db"; @@ -143,6 +159,10 @@ public class TemporaryStorageProvider extends ContentProvider { @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { + if (uri.getLastPathSegment() == null) { + throw new SecurityException("Listing temporary files is not allowed, only querying single files."); + } + File file; try { file = getFile(uri); @@ -153,9 +173,15 @@ public class TemporaryStorageProvider extends ContentProvider { new String[]{uri.getLastPathSegment()}, null, null, null); if (fileName != null) { if (fileName.moveToNext()) { - MatrixCursor cursor = - new MatrixCursor(new String[]{OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE, "_data"}); - cursor.newRow().add(fileName.getString(0)).add(file.length()).add(file.getAbsolutePath()); + MatrixCursor cursor = new MatrixCursor(new String[]{ + OpenableColumns.DISPLAY_NAME, + OpenableColumns.SIZE, + "_data" + }); + cursor.newRow() + .add(fileName.getString(0)) + .add(file.length()) + .add(file.getAbsolutePath()); fileName.close(); return cursor; } @@ -167,8 +193,8 @@ public class TemporaryStorageProvider extends ContentProvider { @Override public String getType(Uri uri) { Cursor cursor = db.getReadableDatabase().query(TABLE_FILES, - new String[]{ COLUMN_TYPE }, COLUMN_ID + "=?", - new String[]{ uri.getLastPathSegment() }, null, null, null); + new String[]{COLUMN_TYPE}, COLUMN_ID + "=?", + new String[]{uri.getLastPathSegment()}, null, null, null); if (cursor != null) { try { if (cursor.moveToNext()) { @@ -180,14 +206,14 @@ public class TemporaryStorageProvider extends ContentProvider { cursor.close(); } } - return "*/*"; + return "application/octet-stream"; } @Override public String[] getStreamTypes(Uri uri, String mimeTypeFilter) { String type = getType(uri); if (ClipDescription.compareMimeTypes(type, mimeTypeFilter)) { - return new String[] { type }; + return new String[]{type}; } return null; } @@ -200,9 +226,14 @@ public class TemporaryStorageProvider extends ContentProvider { String uuid = UUID.randomUUID().toString(); values.put(COLUMN_ID, uuid); int insert = (int) db.getWritableDatabase().insert(TABLE_FILES, null, values); + if (insert == -1) { + Log.e(Constants.TAG, "Insert failed!"); + return null; + } try { getFile(uuid).createNewFile(); } catch (IOException e) { + Log.e(Constants.TAG, "File creation failed!"); return null; } return Uri.withAppendedPath(BASE_URI, uuid); @@ -238,7 +269,7 @@ public class TemporaryStorageProvider extends ContentProvider { throw new UnsupportedOperationException("Update supported only for plain uri!"); } return db.getWritableDatabase().update(TABLE_FILES, values, - COLUMN_ID + " = ?", new String[]{ uri.getLastPathSegment() }); + COLUMN_ID + " = ?", new String[]{uri.getLastPathSegment()}); } @Override |