Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3cae0836 authored by Rhed Jao's avatar Rhed Jao
Browse files

Fix a side channel leakage for the pm#getLaunchIntentSenderForPackage

am#isIntentSenderTargetedToPackage returns false if the target intent
data has a non-null package name and a non-null component name. This
condition hits the successful case of the
pm#getLaunchIntentSenderForPackage api, and the application can
detect package's existence information via both apis. Removes the
package name from the target intent if the lauch intent is found
for the package to fix the issue.

Bug: 228450093
Test: atest AppEnumerationInternalTests
Test: atest PackageManagerTest
Change-Id: I5ec66827eae2a2416e0332694affd2f613a5e965
parent bdfb8336
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -294,6 +294,10 @@ final class ResolveIntentHelper {
        // non-launchable IntentSender which contains the failed intent is created. The
        // SendIntentException is thrown if the IntentSender#sendIntent is invoked.
        if (ris != null && !ris.isEmpty()) {
            // am#isIntentSenderTargetedToPackage returns false if both package name and component
            // name are set in the intent. Clear the package name to have the api return true and
            // prevent the package existence info from side channel leaks by the api.
            intent.setPackage(null);
            intent.setClassName(ris.get(0).activityInfo.packageName,
                    ris.get(0).activityInfo.name);
        }
+26 −0
Original line number Diff line number Diff line
@@ -21,14 +21,19 @@ import static com.android.compatibility.common.util.ShellUtils.runShellCommand;
import static com.google.common.truth.Truth.assertThat;

import android.app.AppGlobals;
import android.app.PendingIntent;
import android.content.Context;
import android.content.IntentSender;
import android.content.pm.IPackageManager;
import android.content.pm.ProviderInfo;
import android.os.Process;
import android.os.UserHandle;

import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -54,6 +59,7 @@ public class AppEnumerationInternalTests {
    private static final String TARGET_HAS_APPOP_PERMISSION =
            "com.android.appenumeration.hasappoppermission";
    private static final String TARGET_SHARED_USER = "com.android.appenumeration.shareduid";
    private static final String TARGET_NON_EXISTENT = "com.android.appenumeration.nonexistent.pkg";

    private static final String SYNC_PROVIDER_AUTHORITY = TARGET_SYNC_PROVIDER;
    private static final String PERMISSION_REQUEST_INSTALL_PACKAGES =
@@ -134,6 +140,26 @@ public class AppEnumerationInternalTests {
        assertThat(uid).isEqualTo(Process.INVALID_UID);
    }

    @Test
    public void getLaunchIntentSenderForPackage_intentSender_cannotDetectPackage()
            throws Exception {
        installPackage(SHARED_USER_APK_PATH, false /* forceQueryable */);

        final Context context = InstrumentationRegistry.getInstrumentation().getContext();
        final IntentSender sender = context.getPackageManager()
                .getLaunchIntentSenderForPackage(TARGET_SHARED_USER);
        assertThat(new PendingIntent(sender.getTarget()).isTargetedToPackage()).isTrue();
        sender.sendIntent(context, 0 /* code */, null /* intent */,
                null /* onFinished */, null /* handler */);

        final IntentSender failedSender = InstrumentationRegistry.getInstrumentation().getContext()
                .getPackageManager().getLaunchIntentSenderForPackage(TARGET_NON_EXISTENT);
        assertThat(new PendingIntent(failedSender.getTarget()).isTargetedToPackage()).isTrue();
        Assert.assertThrows(IntentSender.SendIntentException.class,
                () -> failedSender.sendIntent(context, 0 /* code */, null /* intent */,
                        null /* onFinished */, null /* handler */));
    }

    private static void installPackage(String apkPath, boolean forceQueryable) {
        final StringBuilder cmd = new StringBuilder("pm install ");
        if (forceQueryable) {
+7 −0
Original line number Diff line number Diff line
@@ -19,6 +19,13 @@
          package="com.android.appenumeration.shareduid"
          android:sharedUserId="com.android.appenumeration.shareduid">
    <application>
        <activity android:name="com.android.appenumeration.testapp.DummyActivity"
                  android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.INFO"/>
            </intent-filter>
        </activity>
        <uses-library android:name="android.test.runner" />
    </application>
</manifest>
 No newline at end of file
+22 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.appenumeration.testapp;

import android.app.Activity;

public class DummyActivity extends Activity {
}