From eab6ed8fc9a54d2b528a2da2854b5aa908d76e02 Mon Sep 17 00:00:00 2001 From: bim Date: Thu, 23 Sep 2021 21:53:42 -0400 Subject: [PATCH] Fixes #523 Onion Site Backup Bags This bug was introdued when a previous bug fix pertaining to Onion service storage in Orbot was addressed. The old bug made it such that multiple onion services could be hosted that pointed to the same local port on the device. This previous bug fix broke the backup logic for v2 and v3 onion services, which is fixed here. --- .../HiddenServicesActivity.java | 4 ++- .../ui/hiddenservices/backup/BackupUtils.java | 28 +++++++++---------- .../dialogs/HSActionsDialog.java | 3 +- .../dialogs/HSDeleteDialog.java | 2 +- .../providers/HSContentProvider.java | 4 ++- .../OnionServiceActionsDialogFragment.java | 5 +++- .../v3onionservice/OnionServiceActivity.java | 3 +- .../OnionServiceContentProvider.java | 5 ++-- .../OnionServiceDeleteDialogFragment.java | 2 +- 9 files changed, 33 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/HiddenServicesActivity.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/HiddenServicesActivity.java index 7436eb917..147c4e7e8 100644 --- a/app/src/main/java/org/torproject/android/ui/hiddenservices/HiddenServicesActivity.java +++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/HiddenServicesActivity.java @@ -39,7 +39,8 @@ public class HiddenServicesActivity extends AppCompatActivity { BUNDLE_KEY_PORT = "port", BUNDLE_KEY_ONION = "onion", BUNDLE_KEY_AUTH_COOKIE = "auth_cookie", - BUNDLE_KEY_AUTH_COOKIE_VALUE = "auth_cookie_value"; + BUNDLE_KEY_AUTH_COOKIE_VALUE = "auth_cookie_value", + BUNDLE_KEY_PATH = "path"; private static final int REQUEST_CODE_READ_ZIP_BACKUP = 125; private static final String BUNDLE_KEY_SHOW_USER_SERVICES = "show_user_services"; private ContentResolver mResolver; @@ -83,6 +84,7 @@ protected void onCreate(Bundle savedInstanceState) { arguments.putString(BUNDLE_KEY_ONION, item.getString(item.getColumnIndex(HSContentProvider.HiddenService.DOMAIN))); arguments.putInt(BUNDLE_KEY_AUTH_COOKIE, item.getInt(item.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE))); arguments.putString(BUNDLE_KEY_AUTH_COOKIE_VALUE, item.getString(item.getColumnIndex(HSContentProvider.HiddenService.AUTH_COOKIE_VALUE))); + arguments.putString(BUNDLE_KEY_PATH, item.getString(item.getColumnIndex(HSContentProvider.HiddenService.PATH))); HSActionsDialog dialog = new HSActionsDialog(); dialog.setArguments(arguments); diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/backup/BackupUtils.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/backup/BackupUtils.java index d6cb091cf..64ca1e8da 100644 --- a/app/src/main/java/org/torproject/android/ui/hiddenservices/backup/BackupUtils.java +++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/backup/BackupUtils.java @@ -40,8 +40,8 @@ public BackupUtils(Context context) { mResolver = mContext.getContentResolver(); } - public String createV3ZipBackup(String port, Uri zipFile) { - String[] files = createFilesForZippingV3(port); + public String createV3ZipBackup(String port, String relativePath, Uri zipFile) { + String[] files = createFilesForZippingV3(relativePath); ZipUtilities zip = new ZipUtilities(files, zipFile, mResolver); if (!zip.zip()) return null; return zipFile.getPath(); @@ -61,8 +61,8 @@ public String createV3AuthBackup(String domain, String keyHash, Uri backupFile) return backupFile.getPath(); } - public String createV2ZipBackup(int port, Uri zipFile) { - String[] files = createFilesForZippingV2(port); + public String createV2ZipBackup(int port, String relativePath, Uri zipFile) { + String[] files = createFilesForZippingV2(relativePath); ZipUtilities zip = new ZipUtilities(files, zipFile, mResolver); if (!zip.zip()) @@ -72,15 +72,15 @@ public String createV2ZipBackup(int port, Uri zipFile) { } // todo this doesn't export data for onions that orbot hosts which have authentication (not supported yet...) - private String[] createFilesForZippingV3(String port) { - final String v3BasePath = getV3BasePath() + "/v3" + port + "/"; + private String[] createFilesForZippingV3(String relativePath) { + final String v3BasePath = getV3BasePath() + "/" + relativePath + "/"; final String hostnamePath = v3BasePath + "hostname", configFilePath = v3BasePath + configFileName, privKeyPath = v3BasePath + "hs_ed25519_secret_key", pubKeyPath = v3BasePath + "hs_ed25519_public_key"; Cursor portData = mResolver.query(OnionServiceContentProvider.CONTENT_URI, OnionServiceContentProvider.PROJECTION, - OnionServiceContentProvider.OnionService.PORT + "=" + port, null, null); + OnionServiceContentProvider.OnionService.PATH + "=\"" + relativePath + "\"", null, null); JSONObject config = new JSONObject(); try { @@ -109,16 +109,16 @@ private String[] createFilesForZippingV3(String port) { return new String[]{hostnamePath, configFilePath, privKeyPath, pubKeyPath}; } - private String[] createFilesForZippingV2(int port) { - File hsBasePath = getHSBasePath(); - String configFilePath = hsBasePath + "/hs" + port + "/" + configFileName; - String hostnameFilePath = hsBasePath + "/hs" + port + "/hostname"; - String keyFilePath = hsBasePath + "/hs" + port + "/private_key"; + private String[] createFilesForZippingV2(String relativePath) { + final String hsBasePath = getHSBasePath() + "/" + relativePath + "/"; + String configFilePath = hsBasePath + configFileName; + String hostnameFilePath = hsBasePath + "hostname"; + String keyFilePath = hsBasePath + "private_key"; Cursor portData = mResolver.query( HSContentProvider.CONTENT_URI, HSContentProvider.PROJECTION, - HSContentProvider.HiddenService.PORT + "=" + port, + HSContentProvider.HiddenService.PATH + "=\"" + relativePath + "\"", null, null ); @@ -193,7 +193,7 @@ private void extractConfigFromUnzippedBackupV3(String backupName) { Toast.makeText(mContext, R.string.backup_restored, Toast.LENGTH_LONG).show(); } else { // collision, clean up files - for (File file: v3Path.listFiles()) + for (File file : v3Path.listFiles()) file.delete(); v3Path.delete(); Toast.makeText(mContext, mContext.getString(R.string.backup_port_exist, port), Toast.LENGTH_LONG).show(); diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSActionsDialog.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSActionsDialog.java index 5a7165fe6..8d319324d 100644 --- a/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSActionsDialog.java +++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSActionsDialog.java @@ -107,8 +107,9 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } private void attemptToWriteBackup(Uri outputFile) { + String path = getArguments().getString(HiddenServicesActivity.BUNDLE_KEY_PATH); BackupUtils backupUtils = new BackupUtils(getContext()); - String backup = backupUtils.createV2ZipBackup(port, outputFile); + String backup = backupUtils.createV2ZipBackup(port, path, outputFile); Toast.makeText(getContext(), backup != null ? R.string.backup_saved_at_external_storage : R.string.error, Toast.LENGTH_LONG).show(); dismiss(); } diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSDeleteDialog.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSDeleteDialog.java index f4a96226a..065f68e39 100644 --- a/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSDeleteDialog.java +++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/dialogs/HSDeleteDialog.java @@ -33,7 +33,7 @@ private void doDelete(Bundle arguments, Context context) { // Delete from internal storage String base = context.getFilesDir().getAbsolutePath() + "/" + TorServiceConstants.HIDDEN_SERVICES_DIR; - File dir = new File(base, "hs" + arguments.getString(HiddenServicesActivity.BUNDLE_KEY_PORT)); + File dir = new File(base, arguments.getString(HiddenServicesActivity.BUNDLE_KEY_PATH)); if (dir.isDirectory()) { String[] children = dir.list(); diff --git a/app/src/main/java/org/torproject/android/ui/hiddenservices/providers/HSContentProvider.java b/app/src/main/java/org/torproject/android/ui/hiddenservices/providers/HSContentProvider.java index 79b566fd7..bc4e16b1f 100644 --- a/app/src/main/java/org/torproject/android/ui/hiddenservices/providers/HSContentProvider.java +++ b/app/src/main/java/org/torproject/android/ui/hiddenservices/providers/HSContentProvider.java @@ -24,7 +24,8 @@ public class HSContentProvider extends ContentProvider { HiddenService.AUTH_COOKIE, HiddenService.AUTH_COOKIE_VALUE, HiddenService.CREATED_BY_USER, - HiddenService.ENABLED + HiddenService.ENABLED, + HiddenService.PATH }; private static final String AUTH = "org.torproject.android.ui.hiddenservices.providers"; public static final Uri CONTENT_URI = Uri.parse("content://" + AUTH + "/hs"); @@ -134,6 +135,7 @@ public static final class HiddenService implements BaseColumns { public static final String AUTH_COOKIE_VALUE = "auth_cookie_value"; public static final String CREATED_BY_USER = "created_by_user"; public static final String ENABLED = "enabled"; + public static final String PATH = "filepath"; private HiddenService() { } diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActionsDialogFragment.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActionsDialogFragment.java index 17ce59a76..6c1a8e950 100644 --- a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActionsDialogFragment.java +++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActionsDialogFragment.java @@ -63,6 +63,7 @@ private void doCopy(Bundle arguments, Context context) { private void doBackup(Bundle arguments, Context context) { String filename = "onion_service" + arguments.getString(OnionServiceActivity.BUNDLE_KEY_PORT) + ".zip"; + String relativePath = arguments.getString(OnionServiceActivity.BUNDLE_KEY_PATH); if (arguments.getString(OnionServiceActivity.BUNDLE_KEY_DOMAIN) == null) { Toast.makeText(context, R.string.please_restart_Orbot_to_enable_the_changes, Toast.LENGTH_LONG).show(); return; @@ -85,8 +86,10 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { } private void attemptToWriteBackup(Uri outputFile) { + String port = getArguments().getString(OnionServiceActivity.BUNDLE_KEY_PORT); + String relativePath = getArguments().getString(OnionServiceActivity.BUNDLE_KEY_PATH); BackupUtils backupUtils = new BackupUtils(getContext()); - String backup = backupUtils.createV3ZipBackup(getArguments().getString(OnionServiceActivity.BUNDLE_KEY_PORT), outputFile); + String backup = backupUtils.createV3ZipBackup(port, relativePath, outputFile); Toast.makeText(getContext(), backup != null ? R.string.backup_saved_at_external_storage : R.string.error, Toast.LENGTH_LONG).show(); dismiss(); } diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActivity.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActivity.java index 39db0b106..88f055647 100644 --- a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActivity.java +++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceActivity.java @@ -32,7 +32,7 @@ public class OnionServiceActivity extends AppCompatActivity { - static final String BUNDLE_KEY_ID = "id", BUNDLE_KEY_PORT = "port", BUNDLE_KEY_DOMAIN = "domain"; + static final String BUNDLE_KEY_ID = "id", BUNDLE_KEY_PORT = "port", BUNDLE_KEY_DOMAIN = "domain", BUNDLE_KEY_PATH = "path"; private static final String BASE_WHERE_SELECTION_CLAUSE = OnionServiceContentProvider.OnionService.CREATED_BY_USER + "="; private static final String BUNDLE_KEY_SHOW_USER_SERVICES = "show_user_key"; private static final int REQUEST_CODE_READ_ZIP_BACKUP = 347; @@ -72,6 +72,7 @@ public void onCreate(Bundle bundle) { arguments.putInt(BUNDLE_KEY_ID, item.getInt(item.getColumnIndex(OnionServiceContentProvider.OnionService._ID))); arguments.putString(BUNDLE_KEY_PORT, item.getString(item.getColumnIndex(OnionServiceContentProvider.OnionService.PORT))); arguments.putString(BUNDLE_KEY_DOMAIN, item.getString(item.getColumnIndex(OnionServiceContentProvider.OnionService.DOMAIN))); + arguments.putString(BUNDLE_KEY_PATH, item.getString(item.getColumnIndex(OnionServiceContentProvider.OnionService.PATH))); OnionServiceActionsDialogFragment dialog = new OnionServiceActionsDialogFragment(arguments); dialog.show(getSupportFragmentManager(), OnionServiceActionsDialogFragment.class.getSimpleName()); }); diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceContentProvider.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceContentProvider.java index 61b8540a4..c4ae10ec0 100644 --- a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceContentProvider.java +++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceContentProvider.java @@ -21,7 +21,8 @@ public class OnionServiceContentProvider extends ContentProvider { OnionService.DOMAIN, OnionService.ONION_PORT, OnionService.CREATED_BY_USER, - OnionService.ENABLED + OnionService.ENABLED, + OnionService.PATH }; private static final int ONIONS = 1, ONION_ID = 2; @@ -102,7 +103,7 @@ public static final class OnionService implements BaseColumns { public static final String DOMAIN = "domain"; public static final String CREATED_BY_USER = "created_by_user"; public static final String ENABLED = "enabled"; - + public static final String PATH = "filepath"; private OnionService() { // no-op } } diff --git a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceDeleteDialogFragment.java b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceDeleteDialogFragment.java index 71111fe10..1606c2e40 100644 --- a/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceDeleteDialogFragment.java +++ b/app/src/main/java/org/torproject/android/ui/v3onionservice/OnionServiceDeleteDialogFragment.java @@ -34,7 +34,7 @@ public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) { private void doDelete(Bundle arguments, Context context) { context.getContentResolver().delete(OnionServiceContentProvider.CONTENT_URI, OnionServiceContentProvider.OnionService._ID + '=' + arguments.getInt(OnionServiceActivity.BUNDLE_KEY_ID), null); String base = context.getFilesDir().getAbsolutePath() + "/" + TorServiceConstants.ONION_SERVICES_DIR; - DiskUtils.recursivelyDeleteDirectory(new File(base, "v3" + arguments.getString(OnionServiceActivity.BUNDLE_KEY_PORT))); + DiskUtils.recursivelyDeleteDirectory(new File(base, arguments.getString(OnionServiceActivity.BUNDLE_KEY_PATH))); } }