Skip to content

Commit

Permalink
Fixes #523 Onion Site Backup Bags
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
bitmold committed Sep 24, 2021
1 parent f2ea0b9 commit eab6ed8
Show file tree
Hide file tree
Showing 9 changed files with 33 additions and 23 deletions.
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Expand Up @@ -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();
Expand All @@ -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())
Expand All @@ -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 {
Expand Down Expand Up @@ -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
);
Expand Down Expand Up @@ -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();
Expand Down
Expand Up @@ -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();
}
Expand Down
Expand Up @@ -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();
Expand Down
Expand Up @@ -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");
Expand Down Expand Up @@ -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() {
}
Expand Down
Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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());
});
Expand Down
Expand Up @@ -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;
Expand Down Expand Up @@ -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
}
}
Expand Down
Expand Up @@ -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)));
}

}

0 comments on commit eab6ed8

Please sign in to comment.