android: validate the identity of intent sender
Asked Answered
C

4

8

I work in a company that produces several apps, not all those apps have the same signature or more like it we have at least 5-6 apps certificates for the time being.

We tried to create a mechanism in which all the companie's apps on the same device share the same is, For example if user installed from the market App A and no app installed, a new ID will be generated, if now he installs App A, app B should have the same id as App A(id is just a generated UUID type #4) etc...

We are using broadcast at the moment and only apps with our permission can receive that broadcast and send back the id with another broadcast(explicit this time). The broadcast and the responses are protected with our permission with signature level, this is of course not helping since we have more than one signature.

I tried to write an intent broadcast and recover that can have it's own mechanism of protection that will not be limited to only one signature but several, the problem is that things like Binder.getSenderUID() doesn't work for broadcasts and i get my own uid. it looks like i have no way to get the identity of my snder unless he itself writes his id in the intent, which is NOT something i can trust as it can be easily faked. Using encryption requires the apps to come with a key on them, which is not secured once more, turning to a server for validation takes too much time and on mobile not guaranteed to success since not 100% sure there is network around.

Anyone has any idea how can one get a validate\secure message from one app to another ?(all my apps but may have different signatures).

Coppock answered 20/3, 2013 at 7:37 Comment(0)
R
2

Sorry for late response...

Bind takes time, and more importantly, its asynchronous. However, there is a way to make a synchronous bind - assuming of course the service you attempt to contact is already started at the time. Android allowed for this more for BroadcastReceivers (which are async in nature, and thus can't use normal bindService) a BroadcastReceiver has a "peekService" method.

If you want to use it without listening to a broadcast, you can by doing:

final IBinder[] b = new IBinder[1];
new BroadcastReceiver() { 
    public void onReceive(Context context, Intent intent) {
        b[0] = peekService(context, intent);
    }
}.onReceiver(context, intent);

IMyInterface i = IMyInterface.Stub.asInterface(b[0);

note that you don't bind to the service, so make sure to peek at on each use.

Rica answered 26/3, 2014 at 8:3 Comment(1)
this is a hack, bnut this is damn good, also i assume the service must be local, however as far as i know, BR should be getting messages from the service and send it back or send it to an activity using an intent, it's not meant to bind to a service... so having that method is really breaching the idea of a BR...Coppock
C
6

As always with a challenging question here i never get a proper, if ANY!' answer, so i'm forced to find it myself.

The problem with Intents is that it's not possible to get the sender as they are the parallel to mulicast in a network where there sender's address is not important.

If i wish to get the snder's UID i need to make a "remote" process even if he is local, instead of using broadcast IPC i need to use AIDL with IBInder implementation. Once i have a Binder object i can call in my service getCallingUid() and get the uid of the caller, this will allow me to ask PackageManager to give me it's public certificate (without asking the process itself, i ask the OS) and compare it to a set of certificates i prepared in advance in the apk.

The calling application on the other side(the other process that sends me it's ID) just has to use the bindService(service, conn, flags) method to bind to me. The disadvantage to this approach is ofcourse the time consuming process, Bind takes time, it's an async call that pass through the kernel and is not as fast as binding to a local service. Moreover since i might have several applications i need to synchronize the access my internal ID so only the first binding call that didn't fail will set and ID for me. I still need to check if i can use the Messanger method that prevents the multi thread issues.

Hopes this helps someone else.

Coppock answered 24/3, 2013 at 13:1 Comment(1)
Is there an equivalent solution for broadcast receivers? Or does this work only for services which can be bound...Briquet
R
2

Sorry for late response...

Bind takes time, and more importantly, its asynchronous. However, there is a way to make a synchronous bind - assuming of course the service you attempt to contact is already started at the time. Android allowed for this more for BroadcastReceivers (which are async in nature, and thus can't use normal bindService) a BroadcastReceiver has a "peekService" method.

If you want to use it without listening to a broadcast, you can by doing:

final IBinder[] b = new IBinder[1];
new BroadcastReceiver() { 
    public void onReceive(Context context, Intent intent) {
        b[0] = peekService(context, intent);
    }
}.onReceiver(context, intent);

IMyInterface i = IMyInterface.Stub.asInterface(b[0);

note that you don't bind to the service, so make sure to peek at on each use.

Rica answered 26/3, 2014 at 8:3 Comment(1)
this is a hack, bnut this is damn good, also i assume the service must be local, however as far as i know, BR should be getting messages from the service and send it back or send it to an activity using an intent, it's not meant to bind to a service... so having that method is really breaching the idea of a BR...Coppock
B
0

As already stated, binding is probably the best solution to this. However, you could consider switching to an Activity rather than a BroadcastReceiver then you can use getCallingActivity(), assuming you launched with startActivityForResult().

Declare you Activity as follows to make it "silent" like a BroadcastReceiver:

<activity
    android:name=".FauxReceiver"
    android:theme="@android:style/Theme.NoDisplay" 
    android:excludeFromRecents="true"
    android:noHistory="true"
>
    <intent-filter>
        ...
    </intent-filter>
</activity>

Inspiration: How to get the sender of an Intent?

Bridal answered 4/7, 2014 at 10:16 Comment(2)
We're working on a library for verifying the sender and receiver of Intents that uses this technique to start with, since its the simplest. You can find out more here dev.guardianproject.info/projects/trustedintents/wikiEvergreen
@Hans-ChristophSteiner Looks good. It's odd that they didn't include the sender in all Intent invocations (not just those requiring a result) as the cost would presumably have been minimal.Bridal
C
-2

I was looking for a way to verify the package name of the application that sent the intent received by my intent-filter. That activity in my app that handles the intent-filter requires that the intent sender includes their process id in an Intent Extras field. My receiving activity can then get the associated application package name from the ActivityManager.

Here is some example code I found while shifting through StackOverflow.

Constants needed for both Apps

public static final String EXTRA_APP_ID;
public static final String ACTION_VERIFY = "com.example.receivingapp.action.VERIFY";

Calling Activity

    Intent verifyIntent = new Intent();
    verifyIntent.setAction(Consts.ACTION_VERIFY);
    verifyIntent.putExtra(EXTRA_APP_ID, android.os.Process.myPid());
    // Verify that the intent will resolve to an activity
    if (verifyIntent.resolveActivity(getPackageManager()) != null) {
    startActivityForResult(verifyIntent, Consts.REQUEST_VERIFY);
    } else {
       Log.d(TAG, "Application not found.");
    }

Receiving App

Manifest

        <activity
            android:name="com.example.receivingapp.ReceivingActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="com.example.receivingapp.VERIFY" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>

ReceivingActivity

if (getIntent().hasExtra(OnyxCoreConsts.EXTRA_APP_ID)) {
    string appName = null;  
    // Resolve intent
    if (getIntent().getAction().equals(ACTION_VERIFY) {    
         int appPid = getIntent().getIntExtra(EXTRA_APP_ID, -1);
         if (-1 != mAppPid) {
             appName = Utils.getAppNameByPID(mContext, mAppPid);
         }
         if (null != appName && !"".equalsIgnoreCase(appName)) {
              // Do something with the application package name
         }
    }
}

Utils class

public static String getAppNameByPID(Context context, int pid){
        ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

        for (RunningAppProcessInfo processInfo : manager.getRunningAppProcesses()) {
            if (processInfo.pid == pid) {
                return processInfo.processName;
            }
        }
        return "";
    }
Chunchung answered 12/11, 2014 at 0:21 Comment(3)
What stops a malicious application from inserting the PID of a trusted process?Brooke
@DominicCooney I think that is a valid point and I don't think I could prevent that from happening entirely. I would question how the malicious application could know what the trusted PID's allowed by your application would be.Chunchung
The attacker could use ActivityManager to find the PID of applications with a given name, just like you do. (In all likelihood the attacker started that process anyway using ActivityManager.) Or they can just use /proc which is world readable, at least on my phone. But that may be beside the point; they could also spam intents until they get a match. Edit to add: How would they now the trusted app names, etc.? By disassembling your APK.Brooke

© 2022 - 2024 — McMap. All rights reserved.