Skip to content

Commit 4dd3391

Browse files
committed
use FileHelper.openOutputStreamSafe to resolve output streams
1 parent ba9c93d commit 4dd3391

File tree

8 files changed

+70
-30
lines changed

8 files changed

+70
-30
lines changed

OpenKeychain/src/main/java/org/sufficientlysecure/keychain/operations/BackupOperation.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,9 @@
3232

3333
import android.content.Context;
3434
import android.net.Uri;
35+
3536
import androidx.annotation.NonNull;
3637
import androidx.annotation.Nullable;
37-
3838
import org.bouncycastle.bcpg.ArmoredOutputStream;
3939
import org.sufficientlysecure.keychain.Constants;
4040
import org.sufficientlysecure.keychain.R;
@@ -113,12 +113,12 @@ public ExportResult execute(@NonNull BackupKeyringParcel backupInput, @Nullable
113113
}
114114

115115
plainUri = TemporaryFileProvider.createFile(mContext);
116-
plainOut = mContext.getContentResolver().openOutputStream(plainUri);
116+
plainOut = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), plainUri);
117117
} else {
118118
if (backupInput.getOutputUri() == null || outputStream != null) {
119119
throw new IllegalArgumentException("Unencrypted export to output stream is not supported!");
120120
} else {
121-
plainOut = mContext.getContentResolver().openOutputStream(backupInput.getOutputUri());
121+
plainOut = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), backupInput.getOutputUri());
122122
}
123123
}
124124

@@ -201,7 +201,7 @@ private PgpSignEncryptResult encryptBackupData(@NonNull BackupKeyringParcel back
201201
if (outputStream != null) {
202202
throw new IllegalArgumentException("If output uri is set, outputStream must null!");
203203
}
204-
outStream = mContext.getContentResolver().openOutputStream(backupInput.getOutputUri());
204+
outStream = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), backupInput.getOutputUri());
205205
}
206206

207207
return signEncryptOperation.execute(

OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpDecryptVerifyOperation.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,10 @@
3333
import java.util.Map.Entry;
3434

3535
import android.content.Context;
36-
import androidx.annotation.NonNull;
3736
import android.text.TextUtils;
3837
import android.webkit.MimeTypeMap;
3938

39+
import androidx.annotation.NonNull;
4040
import org.bouncycastle.bcpg.ArmoredInputStream;
4141
import org.bouncycastle.openpgp.PGPCompressedData;
4242
import org.bouncycastle.openpgp.PGPDataValidationException;
@@ -61,6 +61,8 @@
6161
import org.sufficientlysecure.keychain.Constants;
6262
import org.sufficientlysecure.keychain.Constants.key;
6363
import org.sufficientlysecure.keychain.R;
64+
import org.sufficientlysecure.keychain.daos.KeyRepository;
65+
import org.sufficientlysecure.keychain.daos.KeyWritableRepository;
6466
import org.sufficientlysecure.keychain.operations.BaseOperation;
6567
import org.sufficientlysecure.keychain.operations.results.DecryptVerifyResult;
6668
import org.sufficientlysecure.keychain.operations.results.OperationResult.LogType;
@@ -71,8 +73,6 @@
7173
import org.sufficientlysecure.keychain.pgp.SecurityProblem.KeySecurityProblem;
7274
import org.sufficientlysecure.keychain.pgp.SecurityProblem.MissingMdc;
7375
import org.sufficientlysecure.keychain.pgp.exception.PgpGeneralException;
74-
import org.sufficientlysecure.keychain.daos.KeyRepository;
75-
import org.sufficientlysecure.keychain.daos.KeyWritableRepository;
7676
import org.sufficientlysecure.keychain.service.input.CryptoInputParcel;
7777
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel;
7878
import org.sufficientlysecure.keychain.service.input.RequiredInputParcel.RequireAnyDecryptPassphraseBuilder;
@@ -129,7 +129,7 @@ public DecryptVerifyResult execute(PgpDecryptVerifyInputParcel input, CryptoInpu
129129
outputStream = new ByteArrayOutputStream();
130130
} else {
131131
try {
132-
outputStream = mContext.getContentResolver().openOutputStream(input.getOutputUri());
132+
outputStream = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), input.getOutputUri());
133133
} catch (FileNotFoundException e) {
134134
Timber.e(e, "Output URI could not be opened: " + input.getOutputUri());
135135
OperationLog log = new OperationLog();

OpenKeychain/src/main/java/org/sufficientlysecure/keychain/pgp/PgpSignEncryptOperation.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737

3838
import android.content.Context;
3939
import android.net.Uri;
40-
import androidx.annotation.NonNull;
4140

41+
import androidx.annotation.NonNull;
4242
import org.bouncycastle.bcpg.ArmoredOutputStream;
4343
import org.bouncycastle.bcpg.BCPGOutputStream;
4444
import org.bouncycastle.bcpg.CompressionAlgorithmTags;
@@ -143,7 +143,7 @@ public PgpSignEncryptResult execute(PgpSignEncryptInputParcel input, CryptoInput
143143
if (input.getOutputUri() != null) {
144144
try {
145145
Uri outputUri = input.getOutputUri();
146-
outStream = mContext.getContentResolver().openOutputStream(outputUri);
146+
outStream = FileHelper.openOutputStreamSafe(mContext.getContentResolver(), outputUri);
147147
} catch (FileNotFoundException e) {
148148
log.add(LogType.MSG_PSE_ERROR_OUTPUT_URI_NOT_FOUND, 1);
149149
return new PgpSignEncryptResult(SignEncryptResult.RESULT_ERROR, log);

OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptActivity.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,20 @@
2424

2525
import android.app.Activity;
2626
import android.content.ClipData;
27-
import android.content.ClipboardManager;
28-
import android.content.Context;
2927
import android.content.Intent;
3028
import android.net.Uri;
3129
import android.os.Bundle;
30+
import android.widget.Toast;
31+
3232
import androidx.annotation.Nullable;
3333
import androidx.fragment.app.FragmentManager;
3434
import androidx.fragment.app.FragmentTransaction;
35-
import android.widget.Toast;
36-
3735
import org.sufficientlysecure.keychain.Constants;
3836
import org.sufficientlysecure.keychain.R;
3937
import org.sufficientlysecure.keychain.pgp.PgpHelper;
4038
import org.sufficientlysecure.keychain.provider.TemporaryFileProvider;
4139
import org.sufficientlysecure.keychain.ui.base.BaseActivity;
40+
import org.sufficientlysecure.keychain.util.FileHelper;
4241

4342

4443
public class DecryptActivity extends BaseActivity {
@@ -200,7 +199,7 @@ private void handleActions(Bundle savedInstanceState, Intent intent) {
200199
@Nullable
201200
public Uri readToTempFile(String text) throws IOException {
202201
Uri tempFile = TemporaryFileProvider.createFile(this);
203-
OutputStream outStream = getContentResolver().openOutputStream(tempFile);
202+
OutputStream outStream = FileHelper.openOutputStreamSafe(getContentResolver(), tempFile);
204203
if (outStream == null) {
205204
return null;
206205
}

OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/DecryptListFragment.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,6 @@
4444
import android.os.Build.VERSION_CODES;
4545
import android.os.Bundle;
4646
import android.os.Parcelable;
47-
import androidx.annotation.NonNull;
48-
import androidx.core.content.ContextCompat;
49-
import androidx.recyclerview.widget.DefaultItemAnimator;
50-
import androidx.recyclerview.widget.LinearLayoutManager;
51-
import androidx.recyclerview.widget.RecyclerView;
5247
import android.text.TextUtils;
5348
import android.view.LayoutInflater;
5449
import android.view.MenuItem;
@@ -65,8 +60,12 @@
6560
import android.widget.Toast;
6661
import android.widget.ViewAnimator;
6762

63+
import androidx.annotation.NonNull;
64+
import androidx.core.content.ContextCompat;
65+
import androidx.recyclerview.widget.DefaultItemAnimator;
66+
import androidx.recyclerview.widget.LinearLayoutManager;
67+
import androidx.recyclerview.widget.RecyclerView;
6868
import com.cocosw.bottomsheet.BottomSheet;
69-
7069
import org.openintents.openpgp.OpenPgpMetadata;
7170
import org.openintents.openpgp.OpenPgpSignatureResult;
7271
import org.sufficientlysecure.keychain.BuildConfig;
@@ -79,16 +78,15 @@
7978
import org.sufficientlysecure.keychain.pgp.PgpDecryptVerifyInputParcel;
8079
import org.sufficientlysecure.keychain.service.ImportKeyringParcel;
8180
import org.sufficientlysecure.keychain.service.InputDataParcel;
81+
import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel;
82+
import org.sufficientlysecure.keychain.ui.DecryptListFragment.ViewHolder.SubViewHolder;
83+
import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration;
8284
import org.sufficientlysecure.keychain.ui.base.CryptoOperationHelper;
8385
import org.sufficientlysecure.keychain.ui.base.QueueingCryptoOperationFragment;
84-
// this import NEEDS to be above the ViewModel AND SubViewHolder one, or it won't compile! (as of 16.09.15)
8586
import org.sufficientlysecure.keychain.ui.keyview.ViewKeyActivity;
86-
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder;
87-
import org.sufficientlysecure.keychain.ui.DecryptListFragment.ViewHolder.SubViewHolder;
88-
import org.sufficientlysecure.keychain.ui.DecryptListFragment.DecryptFilesAdapter.ViewModel;
89-
import org.sufficientlysecure.keychain.ui.adapter.SpacesItemDecoration;
9087
import org.sufficientlysecure.keychain.ui.util.FormattingUtils;
9188
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils;
89+
import org.sufficientlysecure.keychain.ui.util.KeyFormattingUtils.StatusHolder;
9290
import org.sufficientlysecure.keychain.ui.util.Notify;
9391
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
9492
import org.sufficientlysecure.keychain.util.FileHelper;

OpenKeychain/src/main/java/org/sufficientlysecure/keychain/ui/LogDisplayFragment.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,11 @@
2525
import android.content.Intent;
2626
import android.net.Uri;
2727
import android.os.Bundle;
28-
import androidx.recyclerview.widget.LinearLayoutManager;
2928
import android.view.Menu;
3029
import android.view.MenuInflater;
3130
import android.view.MenuItem;
3231

32+
import androidx.recyclerview.widget.LinearLayoutManager;
3333
import org.sufficientlysecure.keychain.R;
3434
import org.sufficientlysecure.keychain.operations.results.OperationResult;
3535
import org.sufficientlysecure.keychain.operations.results.OperationResult.SubLogEntryParcel;
@@ -39,6 +39,7 @@
3939
import org.sufficientlysecure.keychain.ui.dialog.ShareLogDialogFragment;
4040
import org.sufficientlysecure.keychain.ui.util.Notify;
4141
import org.sufficientlysecure.keychain.ui.util.Notify.Style;
42+
import org.sufficientlysecure.keychain.util.FileHelper;
4243

4344

4445
public class LogDisplayFragment extends RecyclerFragment<NestedLogAdapter>
@@ -120,15 +121,15 @@ private void shareLog() {
120121
if (mLogTempFile == null) {
121122
mLogTempFile = TemporaryFileProvider.createFile(getActivity(), "openkeychain_log.txt", "text/plain");
122123
try {
123-
OutputStream outputStream = activity.getContentResolver().openOutputStream(mLogTempFile);
124+
OutputStream outputStream = FileHelper.openOutputStreamSafe(activity.getContentResolver(), mLogTempFile);
124125
outputStream.write(log.getBytes());
126+
outputStream.close();
125127
} catch (IOException | NullPointerException e) {
126128
Notify.create(activity, R.string.error_log_share_internal, Style.ERROR).show();
127129
return;
128130
}
129131
}
130132

131-
132133
ShareLogDialogFragment shareLogDialog = ShareLogDialogFragment.newInstance(mLogTempFile);
133134
shareLogDialog.show(getActivity().getSupportFragmentManager(), "shareLogDialog");
134135
}

OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelper.java

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ public static void copyUriData(Context context, Uri fromUri, Uri toUri) throws I
299299
try {
300300
ContentResolver resolver = context.getContentResolver();
301301
bis = new BufferedInputStream(FileHelper.openInputStreamSafe(resolver, fromUri));
302-
bos = new BufferedOutputStream(resolver.openOutputStream(toUri));
302+
bos = new BufferedOutputStream(FileHelper.openOutputStreamSafe(resolver, toUri));
303303
byte[] buf = new byte[1024];
304304
int len;
305305
while ( (len = bis.read(buf)) > 0) {
@@ -388,4 +388,14 @@ public static InputStream openInputStreamSafe(ContentResolver resolver, Uri uri)
388388
}
389389
}
390390

391+
public static OutputStream openOutputStreamSafe(ContentResolver resolver, Uri uri)
392+
throws FileNotFoundException {
393+
394+
// Not supported on Android < 5
395+
if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
396+
return FileHelperLollipop.openOutputStreamSafe(resolver, uri);
397+
} else {
398+
return resolver.openOutputStream(uri);
399+
}
400+
}
391401
}

OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/FileHelperLollipop.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import java.io.FileNotFoundException;
2323
import java.io.IOException;
2424
import java.io.InputStream;
25+
import java.io.OutputStream;
2526

2627
import android.content.ContentResolver;
2728
import android.content.res.AssetFileDescriptor;
@@ -88,4 +89,35 @@ static InputStream openInputStreamSafe(ContentResolver resolver, Uri uri)
8889
}
8990

9091
}
92+
93+
static OutputStream openOutputStreamSafe(ContentResolver resolver, Uri uri)
94+
throws FileNotFoundException {
95+
96+
String scheme = uri.getScheme();
97+
if (ContentResolver.SCHEME_FILE.equals(scheme)) {
98+
ParcelFileDescriptor pfd = ParcelFileDescriptor.open(
99+
new File(uri.getPath()), ParcelFileDescriptor.parseMode("w"));
100+
101+
try {
102+
final StructStat st = Os.fstat(pfd.getFileDescriptor());
103+
if (st.st_uid == android.os.Process.myUid()) {
104+
Timber.e("File is owned by the application itself, aborting!");
105+
throw new FileNotFoundException("Unable to create stream");
106+
}
107+
} catch (ErrnoException e) {
108+
Timber.e(e, "fstat() failed");
109+
throw new FileNotFoundException("fstat() failed");
110+
}
111+
112+
AssetFileDescriptor fd = new AssetFileDescriptor(pfd, 0, -1);
113+
try {
114+
return fd.createOutputStream();
115+
} catch (IOException e) {
116+
throw new FileNotFoundException("Unable to create stream");
117+
}
118+
} else {
119+
return resolver.openOutputStream(uri);
120+
}
121+
122+
}
91123
}

0 commit comments

Comments
 (0)