use FileHelper.openOutputStreamSafe to resolve output streams

This commit is contained in:
Vincent Breitmoser
2024-01-29 10:48:53 +01:00
parent ba9c93d046
commit 4dd3391c9f
8 changed files with 70 additions and 30 deletions

View File

@@ -32,9 +32,9 @@ import java.util.regex.Pattern;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
@@ -113,12 +113,12 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
} }
plainUri = TemporaryFileProvider.createFile(mContext); plainUri = TemporaryFileProvider.createFile(mContext);
plainOut = mContext.getContentResolver().openOutputStream(plainUri); plainOut = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), plainUri);
} else { } else {
if (backupInput.getOutputUri() == null || outputStream != null) { if (backupInput.getOutputUri() == null || outputStream != null) {
throw new IllegalArgumentException("Unencrypted export to output stream is not supported!"); throw new IllegalArgumentException("Unencrypted export to output stream is not supported!");
} else { } else {
plainOut = mContext.getContentResolver().openOutputStream(backupInput.getOutputUri()); plainOut = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), backupInput.getOutputUri());
} }
} }
@@ -201,7 +201,7 @@ public class BackupOperation extends BaseOperation<BackupKeyringParcel> {
if (outputStream != null) { if (outputStream != null) {
throw new IllegalArgumentException("If output uri is set, outputStream must null!"); throw new IllegalArgumentException("If output uri is set, outputStream must null!");
} }
outStream = mContext.getContentResolver().openOutputStream(backupInput.getOutputUri()); outStream = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), backupInput.getOutputUri());
} }
return signEncryptOperation.execute( return signEncryptOperation.execute(

View File

@@ -33,10 +33,10 @@ import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
import android.content.Context; import android.content.Context;
import androidx.annotation.NonNull;
import android.text.TextUtils; import android.text.TextUtils;
import android.webkit.MimeTypeMap; import android.webkit.MimeTypeMap;
import androidx.annotation.NonNull;
import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPCompressedData;
import org.bouncycastle.openpgp.PGPDataValidationException; import org.bouncycastle.openpgp.PGPDataValidationException;
@@ -61,6 +61,8 @@ import org.openintents.openpgp.OpenPgpMetadata;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.Constants.key; import org.sufficientlysecure.keychain.Constants.key;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.daos.KeyRepository;
import org.sufficientlysecure.keychain.daos.KeyWritableRepository;
import org.sufficientlysecure.keychain.operations.BaseOperation; import org.sufficientlysecure.keychain.operations.BaseOperation;
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult; import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType; import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
@@ -71,8 +73,6 @@ import org.sufficientlysecure.keychain.pgp.SecurityProblem.EncryptionAlgorithmPr
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem; import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc; import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException; import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
import org.sufficientlysecure.keychain.daos.KeyRepository;
import org.sufficientlysecure.keychain.daos.KeyWritableRepository;
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel; import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequireAnyDecryptPassphraseBuilder; import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequireAnyDecryptPassphraseBuilder;
@@ -129,7 +129,7 @@ public class PgpDecryptVerifyOperation extends BaseOperation<PgpDecryptVerifyInp
outputStream = new ByteArrayOutputStream(); outputStream = new ByteArrayOutputStream();
} else { } else {
try { try {
outputStream = mContext.getContentResolver().openOutputStream(input.getOutputUri()); outputStream = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), input.getOutputUri());
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
Timber.e(e, "Output URI could not be opened: " + input.getOutputUri()); Timber.e(e, "Output URI could not be opened: " + input.getOutputUri());
OperationLog log = new OperationLog(); OperationLog log = new OperationLog();

View File

@@ -37,8 +37,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
import android.content.Context; import android.content.Context;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull;
import androidx.annotation.NonNull;
import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.bouncycastle.bcpg.BCPGOutputStream; import org.bouncycastle.bcpg.BCPGOutputStream;
import org.bouncycastle.bcpg.CompressionAlgorithmTags; import org.bouncycastle.bcpg.CompressionAlgorithmTags;
@@ -143,7 +143,7 @@ public class PgpSignEncryptOperation extends BaseOperation<PgpSignEncryptInputPa
if (input.getOutputUri() != null) { if (input.getOutputUri() != null) {
try { try {
Uri outputUri = input.getOutputUri(); Uri outputUri = input.getOutputUri();
outStream = mContext.getContentResolver().openOutputStream(outputUri); outStream = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), outputUri);
} catch (FileNotFoundException e) { } catch (FileNotFoundException e) {
log.add(LogType.MSG_PSE_ERROR_OUTPUT_URI_NOT_FOUND, 1); log.add(LogType.MSG_PSE_ERROR_OUTPUT_URI_NOT_FOUND, 1);
return new PgpSignEncryptResult(SignEncryptResult.RESULT_ERROR, log); return new PgpSignEncryptResult(SignEncryptResult.RESULT_ERROR, log);

View File

@@ -24,21 +24,20 @@ import java.util.ArrayList;
import android.app.Activity; import android.app.Activity;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.widget.Toast;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import android.widget.Toast;
import org.sufficientlysecure.keychain.Constants; import org.sufficientlysecure.keychain.Constants;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.pgp.PgpHelper; import org.sufficientlysecure.keychain.pgp.PgpHelper;
import org.sufficientlysecure.keychain.provider.TemporaryFileProvider; import org.sufficientlysecure.keychain.provider.TemporaryFileProvider;
import org.sufficientlysecure.keychain.ui.base.BaseActivity; import org.sufficientlysecure.keychain.ui.base.BaseActivity;
import org.sufficientlysecure.keychain.util.FileHelper;
public class DecryptActivity extends BaseActivity { public class DecryptActivity extends BaseActivity {
@@ -200,7 +199,7 @@ public class DecryptActivity extends BaseActivity {
@Nullable @Nullable
public Uri readToTempFile(String text) throws IOException { public Uri readToTempFile(String text) throws IOException {
Uri tempFile = TemporaryFileProvider.createFile(this); Uri tempFile = TemporaryFileProvider.createFile(this);
OutputStream outStream = getContentResolver().openOutputStream(tempFile); OutputStream outStream = FileHelper.openOutputStreamSafe(getContentResolver(), tempFile);
if (outStream == null) { if (outStream == null) {
return null; return null;
} }

View File

@@ -44,11 +44,6 @@ import android.os.Build;
import android.os.Build.VERSION_CODES; import android.os.Build.VERSION_CODES;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.text.TextUtils; import android.text.TextUtils;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuItem; import android.view.MenuItem;
@@ -65,8 +60,12 @@ import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import android.widget.ViewAnimator; import android.widget.ViewAnimator;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.cocosw.bottomsheet.BottomSheet; import com.cocosw.bottomsheet.BottomSheet;
import org.openintents.openpgp.OpenPgpMetadata; import org.openintents.openpgp.OpenPgpMetadata;
import org.openintents.openpgp.OpenPgpSignatureResult; import org.openintents.openpgp.OpenPgpSignatureResult;
import org.sufficientlysecure.keychain.BuildConfig; import org.sufficientlysecure.keychain.BuildConfig;
@@ -79,16 +78,15 @@ import org.sufficientlysecure.keychain.operations.results.InputDataResult;
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel; import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
import org.sufficientlysecure.keychain.service.ImportKeyringParcel; import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
import org.sufficientlysecure.keychain.service.InputDataParcel; import org.sufficientlysecure.keychain.service.InputDataParcel;
import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel;
import org.sufficientlysecure.keychain.ui.DecryptListFragment.ViewHolder.SubViewHolder;
import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration;
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper; import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment; import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
// this import NEEDS to be above the ViewModel AND SubViewHolder one, or it won't compile! (as of 16.09.15)
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity; import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder;
import org.sufficientlysecure.keychain.ui.DecryptListFragment.ViewHolder.SubViewHolder;
import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel;
import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration;
import org.sufficientlysecure.keychain.ui.util.FormattingUtils; import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils; import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.FileHelper; import org.sufficientlysecure.keychain.util.FileHelper;

View File

@@ -25,11 +25,11 @@ import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import androidx.recyclerview.widget.LinearLayoutManager;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import androidx.recyclerview.widget.LinearLayoutManager;
import org.sufficientlysecure.keychain.R; import org.sufficientlysecure.keychain.R;
import org.sufficientlysecure.keychain.operations.results.OperationResult; import org.sufficientlysecure.keychain.operations.results.OperationResult;
import org.sufficientlysecure.keychain.operations.results.OperationResult.SubLogEntryParcel; import org.sufficientlysecure.keychain.operations.results.OperationResult.SubLogEntryParcel;
@@ -39,6 +39,7 @@ import org.sufficientlysecure.keychain.ui.base.RecyclerFragment;
import org.sufficientlysecure.keychain.ui.dialog.ShareLogDialogFragment; import org.sufficientlysecure.keychain.ui.dialog.ShareLogDialogFragment;
import org.sufficientlysecure.keychain.ui.util.Notify; import org.sufficientlysecure.keychain.ui.util.Notify;
import org.sufficientlysecure.keychain.ui.util.Notify.Style; import org.sufficientlysecure.keychain.ui.util.Notify.Style;
import org.sufficientlysecure.keychain.util.FileHelper;
public class LogDisplayFragment extends RecyclerFragment<NestedLogAdapter> public class LogDisplayFragment extends RecyclerFragment<NestedLogAdapter>
@@ -120,15 +121,15 @@ public class LogDisplayFragment extends RecyclerFragment<NestedLogAdapter>
if (mLogTempFile == null) { if (mLogTempFile == null) {
mLogTempFile = TemporaryFileProvider.createFile(getActivity(), "openkeychain_log.txt", "text/plain"); mLogTempFile = TemporaryFileProvider.createFile(getActivity(), "openkeychain_log.txt", "text/plain");
try { try {
OutputStream outputStream = activity.getContentResolver().openOutputStream(mLogTempFile); OutputStream outputStream = FileHelper.openOutputStreamSafe(activity.getContentResolver(), mLogTempFile);
outputStream.write(log.getBytes()); outputStream.write(log.getBytes());
outputStream.close();
} catch (IOException | NullPointerException e) { } catch (IOException | NullPointerException e) {
Notify.create(activity, R.string.error_log_share_internal, Style.ERROR).show(); Notify.create(activity, R.string.error_log_share_internal, Style.ERROR).show();
return; return;
} }
} }
ShareLogDialogFragment shareLogDialog = ShareLogDialogFragment.newInstance(mLogTempFile); ShareLogDialogFragment shareLogDialog = ShareLogDialogFragment.newInstance(mLogTempFile);
shareLogDialog.show(getActivity().getSupportFragmentManager(), "shareLogDialog"); shareLogDialog.show(getActivity().getSupportFragmentManager(), "shareLogDialog");
} }

View File

@@ -299,7 +299,7 @@ public class FileHelper {
try { try {
ContentResolver resolver = context.getContentResolver(); ContentResolver resolver = context.getContentResolver();
bis = new BufferedInputStream(FileHelper.openInputStreamSafe(resolver, fromUri)); bis = new BufferedInputStream(FileHelper.openInputStreamSafe(resolver, fromUri));
bos = new BufferedOutputStream(resolver.openOutputStream(toUri)); bos = new BufferedOutputStream(FileHelper.openOutputStreamSafe(resolver, toUri));
byte[] buf = new byte[1024]; byte[] buf = new byte[1024];
int len; int len;
while ( (len = bis.read(buf)) > 0) { while ( (len = bis.read(buf)) > 0) {
@@ -388,4 +388,14 @@ public class FileHelper {
} }
} }
public static OutputStream openOutputStreamSafe(ContentResolver resolver, Uri uri)
throws FileNotFoundException {
// Not supported on Android < 5
if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
return FileHelperLollipop.openOutputStreamSafe(resolver, uri);
} else {
return resolver.openOutputStream(uri);
}
}
} }

View File

@@ -22,6 +22,7 @@ import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.res.AssetFileDescriptor; import android.content.res.AssetFileDescriptor;
@@ -88,4 +89,35 @@ class FileHelperLollipop {
} }
} }
static OutputStream openOutputStreamSafe(ContentResolver resolver, Uri uri)
throws FileNotFoundException {
String scheme = uri.getScheme();
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
new File(uri.getPath()), ParcelFileDescriptor.parseMode("w"));
try {
final StructStat st = Os.fstat(pfd.getFileDescriptor());
if (st.st_uid == android.os.Process.myUid()) {
Timber.e("File is owned by the application itself, aborting!");
throw new FileNotFoundException("Unable to create stream");
}
} catch (ErrnoException e) {
Timber.e(e, "fstat() failed");
throw new FileNotFoundException("fstat() failed");
}
AssetFileDescriptor fd = new AssetFileDescriptor(pfd, 0, -1);
try {
return fd.createOutputStream();
} catch (IOException e) {
throw new FileNotFoundException("Unable to create stream");
}
} else {
return resolver.openOutputStream(uri);
}
}
} }