Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct permission handling when sending sensitive app data as email attachment?

Tags:

android

I am having trouble granting "reverse permissions" for apps that I wish to provide with sensitive data in a controlled manner.

My application is a time tracker and, because the time-tracking log could be considered personal information, I have created a permission for accessing it and assigned it the android.permission- group.PERSONAL_INFO permission group.

To export the time log from the phone I am adding the ability to send the log as an email attachment. The attachment is generated by a content provider that is protected by my newly added permission. My code for sending the email looks like this:

   String email = "[email protected]";
   Uri uri = TimeLog.CSVAttachment.CONTENT_URI;
   Intent i = new Intent(Intent.ACTION_SEND, uri);
   i.setType("text/csv");
   i.putExtra(Intent.EXTRA_EMAIL, new String[]{email});
   i.putExtra(Intent.EXTRA_SUBJECT, "Time log");
   i.putExtra(Intent.EXTRA_TEXT, "Hello World!");
   i.putExtra(Intent.EXTRA_STREAM, uri);
   i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
   startActivity(i);

When running it on my HTC phone, I get a pop-up choice between Gmail and HTC mail. Choosing Gmail, I get this exception in the Gmail app:

ERROR/AndroidRuntime(8169): Caused by: java.lang.SecurityException:
Permission Denial: reading com.mycompany.timelog.TimeLog uri
content://com.mycompany.timelog/csv_attachment from pid=8169,
uid=10035 requires com.mycompany.timelog.permission.READ_TIME_LOG

I do have android:grantUriPermissions="true" set on my provider but that's not helping. I have a theory about why this happens. I had expected FLAG_GRANT_READ_URI_PERMISSION to give Gmail the right to access my content provider, but I think what really happens is that this permission is granted to com.android.internal.app.ResolverActivity because there is more than one match for the Intent and Android creates a wrapper activity for displaying the choice to the user.

So, I've tried hard-coding this into my app just for testing:

   grantUriPermission("com.google.android.gm", uri,
       Intent.FLAG_GRANT_READ_URI_PERMISSION);

This allows Gmail to display the email correctly and I can press "Send". Unfortunately, after GMail has closed I get this exception in com.google.process.gapps:

ERROR/AndroidRuntime(7617): java.lang.SecurityException: Permission Denial: reading com.mycompany.timelog.TimeLog uri content://com.mycompany.timelog/csv_attachment from pid=7617, uid=10011 requires com.mycompany.timelog.permission.READ_TIME_LOG

Note that this is coming from a different PID and UID. This is because the actual call to openAssetFile happens from some sync provider component that belongs to a different package (com.google.android.googleapps?).

While I had some hope of eventually finding a way to grant permissions to the final receiver of my ACTION_SEND intent, the fact that the call to openAssetFile happens from some entirely different and practically unrelated package leaves me baffled as to how permission granting is supposed to work.

So ultimately my question is, given that the log is sensitive data, how would I allow it to be emailed as an attachment while honoring the privacy of the user (e.g. without making the attachment world readable)?

like image 655
flodin Avatar asked Nov 14 '10 20:11

flodin


2 Answers

Dear people from the future,

It seems even google itself solves this problem in another way which i stumbled upon while trying to solve this same problem.

If you look at com.android.contacts.detail.ContactLoaderFragment you find in the method private Uri getPreAuthorizedUri(Uri uri):

mContext.getContentResolver().call(
            ContactsContract.AUTHORITY_URI,
            ContactsContract.Authorization.AUTHORIZATION_METHOD,
            null,
            uriBundle);

Which resolves to com.android.providers.contacts.ContactsProvider2 where a similar call method add the uri to a map mPreAuthorizedUris which is used in the query/update/...-methods.

The return value of that call is put in the Intent and then used.

like image 163
Steven Verborgh Avatar answered Nov 20 '22 21:11

Steven Verborgh


This is a nice approach, unfortunately as you are seeing there are probably a number of issues you will run in to in the framework that prevent you from doing it. There are currently some issues with granting uri permissions that make that functionality less useful than it should be (many of these will be addressed in Gingerbread), and on top of that Gmail just doesn't seem to expect this to happen and deal with retaining the granted permission as long as it needs to.

How large is this data? If it isn't too large, how about just including it directing in the Intent as a string?

like image 1
hackbod Avatar answered Nov 20 '22 20:11

hackbod