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

Commit f0b2e92d authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Abort or throw SecurityException if intent redirect detected" into main

parents c5923a4d dd704c1b
Loading
Loading
Loading
Loading
+19 −3
Original line number Diff line number Diff line
@@ -40,7 +40,10 @@ import android.app.Activity;
import android.app.ActivityThread;
import android.app.AppGlobals;
import android.app.StatusBarManager;
import android.app.compat.CompatChanges;
import android.bluetooth.BluetoothDevice;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Overridable;
import android.compat.annotation.UnsupportedAppUsage;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -670,6 +673,11 @@ import java.util.TimeZone;
public class Intent implements Parcelable, Cloneable {
    private static final String TAG = "Intent";
    /** @hide */
    @ChangeId
    @Overridable
    public static final long ENABLE_PREVENT_INTENT_REDIRECT = 29076063L;
    private static final String ATTR_ACTION = "action";
    private static final String TAG_CATEGORIES = "categories";
    private static final String ATTR_CATEGORY = "category";
@@ -12240,7 +12248,7 @@ public class Intent implements Parcelable, Cloneable {
     * @hide
     */
    public void collectExtraIntentKeys() {
        if (!preventIntentRedirect()) return;
        if (!isPreventIntentRedirectEnabled()) return;
        if (mExtras != null && !mExtras.isParcelled() && !mExtras.isEmpty()) {
            for (String key : mExtras.keySet()) {
@@ -12257,6 +12265,14 @@ public class Intent implements Parcelable, Cloneable {
        }
    }
    /**
     * @hide
     */
    public static boolean isPreventIntentRedirectEnabled() {
        return preventIntentRedirect() && CompatChanges.isChangeEnabled(
                ENABLE_PREVENT_INTENT_REDIRECT);
    }
    /** @hide */
    public void checkCreatorToken() {
        if (mExtras == null) return;
@@ -12331,7 +12347,7 @@ public class Intent implements Parcelable, Cloneable {
            out.writeInt(0);
        }
        if (preventIntentRedirect()) {
        if (isPreventIntentRedirectEnabled()) {
            if (mCreatorTokenInfo == null) {
                out.writeInt(0);
            } else {
@@ -12398,7 +12414,7 @@ public class Intent implements Parcelable, Cloneable {
            mOriginalIntent = new Intent(in);
        }
        if (preventIntentRedirect()) {
        if (isPreventIntentRedirectEnabled()) {
            if (in.readInt() != 0) {
                mCreatorTokenInfo = new CreatorTokenInfo();
                mCreatorTokenInfo.mCreatorToken = in.readStrongBinder();
+7 −0
Original line number Diff line number Diff line
@@ -78,3 +78,10 @@ flag {
    bug: "361143368"
    is_fixed_read_only: true
}

flag {
    name: "prevent_intent_redirect_abort_or_throw_exception"
    namespace: "responsible_apis"
    description: "Prevent intent redirect attacks by aborting or throwing security exception"
    bug: "361143368"
}
 No newline at end of file
+5 −3
Original line number Diff line number Diff line
@@ -60,6 +60,7 @@ import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_BACKUP;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_INSTRUMENTATION;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_PERSISTENT;
import static android.app.ProcessMemoryState.HOSTING_COMPONENT_TYPE_SYSTEM;
import static android.content.Intent.isPreventIntentRedirectEnabled;
import static android.content.pm.ApplicationInfo.HIDDEN_API_ENFORCEMENT_DEFAULT;
import static android.content.pm.PackageManager.GET_SHARED_LIBRARY_FILES;
import static android.content.pm.PackageManager.MATCH_ALL;
@@ -130,7 +131,6 @@ import static android.os.Process.setThreadScheduler;
import static android.provider.Settings.Global.ALWAYS_FINISH_ACTIVITIES;
import static android.provider.Settings.Global.DEBUG_APP;
import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
import static android.security.Flags.preventIntentRedirect;
import static android.util.FeatureFlagUtils.SETTINGS_ENABLE_MONITOR_PHANTOM_PROCS;
import static android.view.Display.INVALID_DISPLAY;
@@ -19272,7 +19272,7 @@ public class ActivityManagerService extends IActivityManager.Stub
     * @hide
     */
    public void addCreatorToken(@Nullable Intent intent, String creatorPackage) {
        if (!preventIntentRedirect()) return;
        if (!isPreventIntentRedirectEnabled()) return;
        if (intent == null || intent.getExtraIntentKeys() == null) return;
        for (String key : intent.getExtraIntentKeys()) {
@@ -19283,7 +19283,9 @@ public class ActivityManagerService extends IActivityManager.Stub
                            + "} does not correspond to an intent in the extra bundle.");
                    continue;
                }
                Slog.wtf(TAG, "A creator token is added to an intent.");
                Slog.wtf(TAG,
                        "A creator token is added to an intent. creatorPackage: " + creatorPackage
                                + "; intent: " + intent);
                IBinder creatorToken = createIntentCreatorToken(extraIntent, creatorPackage);
                if (creatorToken != null) {
                    extraIntent.setCreatorToken(creatorToken);
+3 −3
Original line number Diff line number Diff line
@@ -478,10 +478,10 @@ public class ActivityStartController {
                                intentGrants.merge(creatorIntentGrants);
                            }
                        } catch (SecurityException securityException) {
                            ActivityStarter.logForIntentRedirect(
                            ActivityStarter.logAndThrowExceptionForIntentRedirect(
                                    "Creator URI Grant Caused Exception.", intent, creatorUid,
                                    creatorPackage, filterCallingUid, callingPackage);
                            // TODO b/368559093 - rethrow the securityException.
                                    creatorPackage, filterCallingUid, callingPackage,
                                    securityException);
                        }
                    }
                    if ((aInfo.applicationInfo.privateFlags
+65 −52
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
import static android.content.pm.ActivityInfo.launchModeToString;
import static android.os.Process.INVALID_UID;
import static android.security.Flags.preventIntentRedirectAbortOrThrowException;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.WindowManager.TRANSIT_NONE;
import static android.view.WindowManager.TRANSIT_OPEN;
@@ -100,9 +101,11 @@ import android.app.PendingIntent;
import android.app.ProfilerInfo;
import android.app.WaitResult;
import android.app.WindowConfiguration;
import android.app.compat.CompatChanges;
import android.compat.annotation.ChangeId;
import android.compat.annotation.Disabled;
import android.compat.annotation.EnabledSince;
import android.compat.annotation.Overridable;
import android.content.IIntentSender;
import android.content.Intent;
import android.content.IntentSender;
@@ -188,6 +191,10 @@ class ActivityStarter {
    @Disabled
    static final long ASM_RESTRICTIONS = 230590090L;

    @ChangeId
    @Overridable
    private static final long ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION = 29623414L;

    private final ActivityTaskManagerService mService;
    private final RootWindowContainer mRootWindowContainer;
    private final ActivityTaskSupervisor mSupervisor;
@@ -608,11 +615,10 @@ class ActivityStarter {
            // Check if the Intent was redirected
            if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN)
                    != 0) {
                ActivityStarter.logForIntentRedirect(
                ActivityStarter.logAndThrowExceptionForIntentRedirect(
                        "Unparceled intent does not have a creator token set.", intent,
                        intentCreatorUid,
                        intentCreatorPackage, resolvedCallingUid, resolvedCallingPackage);
                // TODO b/368559093 - eventually ramp up to throw SecurityException
                        intentCreatorUid, intentCreatorPackage, resolvedCallingUid,
                        resolvedCallingPackage, null);
            }
            if (IntentCreatorToken.isValid(intent)) {
                IntentCreatorToken creatorToken = (IntentCreatorToken) intent.getCreatorToken();
@@ -645,11 +651,10 @@ class ActivityStarter {
                                intentGrants.merge(creatorIntentGrants);
                            }
                        } catch (SecurityException securityException) {
                            ActivityStarter.logForIntentRedirect(
                            ActivityStarter.logAndThrowExceptionForIntentRedirect(
                                    "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
                                    intentCreatorPackage, resolvedCallingUid,
                                    resolvedCallingPackage);
                            // TODO b/368559093 - rethrow the securityException.
                                    resolvedCallingPackage, securityException);
                        }
                    }
                } else {
@@ -670,11 +675,10 @@ class ActivityStarter {
                                intentGrants.merge(creatorIntentGrants);
                            }
                        } catch (SecurityException securityException) {
                            ActivityStarter.logForIntentRedirect(
                            ActivityStarter.logAndThrowExceptionForIntentRedirect(
                                    "Creator URI Grant Caused Exception.", intent, intentCreatorUid,
                                    intentCreatorPackage, resolvedCallingUid,
                                    resolvedCallingPackage);
                            // TODO b/368559093 - rethrow the securityException.
                                    resolvedCallingPackage, securityException);
                        }
                    }
                }
@@ -1045,7 +1049,7 @@ class ActivityStarter {
        int callingUid = request.callingUid;
        int intentCreatorUid = request.intentCreatorUid;
        String intentCreatorPackage = request.intentCreatorPackage;
        String intentCallingPackage = request.callingPackage;
        String callingPackage = request.callingPackage;
        String callingFeatureId = request.callingFeatureId;
        final int realCallingPid = request.realCallingPid;
        final int realCallingUid = request.realCallingUid;
@@ -1130,7 +1134,7 @@ class ActivityStarter {
                // launched in the app flow to redirect to an activity picked by the user, where
                // we want the final activity to consider it to have been launched by the
                // previous app activity.
                intentCallingPackage = sourceRecord.launchedFromPackage;
                callingPackage = sourceRecord.launchedFromPackage;
                callingFeatureId = sourceRecord.launchedFromFeatureId;
            }
        }
@@ -1152,7 +1156,7 @@ class ActivityStarter {
                if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) {
                    err = packageArchiver
                            .requestUnarchiveOnActivityStart(
                                    intent, intentCallingPackage, mRequest.userId, realCallingUid);
                                    intent, callingPackage, mRequest.userId, realCallingUid);
                }
            }
        }
@@ -1211,7 +1215,7 @@ class ActivityStarter {
        boolean abort;
        try {
            abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho,
                    requestCode, callingPid, callingUid, intentCallingPackage, callingFeatureId,
                    requestCode, callingPid, callingUid, callingPackage, callingFeatureId,
                    request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord,
                    resultRootTask);
        } catch (SecurityException e) {
@@ -1239,7 +1243,7 @@ class ActivityStarter {
        abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid,
                callingPid, resolvedType, aInfo.applicationInfo);
        abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid,
                intentCallingPackage);
                callingPackage);

        if (intentCreatorUid != Request.DEFAULT_INTENT_CREATOR_UID) {
            try {
@@ -1247,36 +1251,29 @@ class ActivityStarter {
                        requestCode, 0, intentCreatorUid, intentCreatorPackage, "",
                        request.ignoreTargetSecurity, inTask != null, null, resultRecord,
                        resultRootTask)) {
                    logForIntentRedirect("Creator checkStartAnyActivityPermission Caused abortion.",
                    abort = logAndAbortForIntentRedirect(
                            "Creator checkStartAnyActivityPermission Caused abortion.",
                            intent, intentCreatorUid, intentCreatorPackage, callingUid,
                            intentCallingPackage);
                    // TODO b/368559093 - set abort to true.
                    // abort = true;
                            callingPackage);
                }
            } catch (SecurityException e) {
                logForIntentRedirect("Creator checkStartAnyActivityPermission Caused Exception.",
                        intent, intentCreatorUid, intentCreatorPackage, callingUid,
                        intentCallingPackage);
                // TODO b/368559093 - rethrow the exception.
                //throw e;
                logAndThrowExceptionForIntentRedirect(
                        "Creator checkStartAnyActivityPermission Caused Exception.",
                        intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage,
                        e);
            }
            if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid, 0,
                    resolvedType, aInfo.applicationInfo)) {
                logForIntentRedirect("Creator IntentFirewall.checkStartActivity Caused abortion.",
                        intent, intentCreatorUid, intentCreatorPackage, callingUid,
                        intentCallingPackage);
                // TODO b/368559093 - set abort to true.
                // abort = true;
            if (!mService.mIntentFirewall.checkStartActivity(intent, intentCreatorUid,
                    0, resolvedType, aInfo.applicationInfo)) {
                abort = logAndAbortForIntentRedirect(
                        "Creator IntentFirewall.checkStartActivity Caused abortion.",
                        intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
            }

            if (!mService.getPermissionPolicyInternal().checkStartActivity(intent, intentCreatorUid,
                    intentCreatorPackage)) {
                logForIntentRedirect(
            if (!mService.getPermissionPolicyInternal().checkStartActivity(intent,
                    intentCreatorUid, intentCreatorPackage)) {
                abort = logAndAbortForIntentRedirect(
                        "Creator PermissionPolicyService.checkStartActivity Caused abortion.",
                        intent, intentCreatorUid, intentCreatorPackage, callingUid,
                        intentCallingPackage);
                // TODO b/368559093 - set abort to true.
                // abort = true;
                        intent, intentCreatorUid, intentCreatorPackage, callingUid, callingPackage);
            }
            intent.removeCreatorTokenInfo();
        }
@@ -1296,7 +1293,7 @@ class ActivityStarter {
                        balController.checkBackgroundActivityStart(
                            callingUid,
                            callingPid,
                                intentCallingPackage,
                            callingPackage,
                            realCallingUid,
                            realCallingPid,
                            callerApp,
@@ -1317,7 +1314,7 @@ class ActivityStarter {
        if (request.allowPendingRemoteAnimationRegistryLookup) {
            checkedOptions = mService.getActivityStartController()
                    .getPendingRemoteAnimationRegistry()
                    .overrideOptionsIfNeeded(intentCallingPackage, checkedOptions);
                    .overrideOptionsIfNeeded(callingPackage, checkedOptions);
        }
        if (mService.mController != null) {
            try {
@@ -1334,7 +1331,7 @@ class ActivityStarter {
        final TaskDisplayArea suggestedLaunchDisplayArea =
                computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions);
        mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags,
                intentCallingPackage,
                callingPackage,
                callingFeatureId);
        if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment,
                callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) {
@@ -1372,7 +1369,7 @@ class ActivityStarter {
            if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired(
                    aInfo.packageName, userId)) {
                final IIntentSender target = mService.getIntentSenderLocked(
                        ActivityManager.INTENT_SENDER_ACTIVITY, intentCallingPackage,
                        ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage,
                        callingFeatureId,
                        callingUid, userId, null, null, 0, new Intent[]{intent},
                        new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT
@@ -1436,7 +1433,7 @@ class ActivityStarter {
        // app [on install success].
        if (rInfo != null && rInfo.auxiliaryInfo != null) {
            intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent,
                    intentCallingPackage, callingFeatureId, verificationBundle, resolvedType,
                    callingPackage, callingFeatureId, verificationBundle, resolvedType,
                    userId);
            resolvedType = null;
            callingUid = realCallingUid;
@@ -1460,7 +1457,7 @@ class ActivityStarter {
                .setCaller(callerApp)
                .setLaunchedFromPid(callingPid)
                .setLaunchedFromUid(callingUid)
                .setLaunchedFromPackage(intentCallingPackage)
                .setLaunchedFromPackage(callingPackage)
                .setLaunchedFromFeature(callingFeatureId)
                .setIntent(intent)
                .setResolvedType(resolvedType)
@@ -3588,16 +3585,32 @@ class ActivityStarter {
        pw.println(mInTaskFragment);
    }

    static void logForIntentRedirect(String message, Intent intent, int intentCreatorUid,
            String intentCreatorPackage, int callingUid, String callingPackage) {
    static void logAndThrowExceptionForIntentRedirect(@NonNull String message,
            @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
            int callingUid, @Nullable String callingPackage,
            @Nullable SecurityException originalException) {
        String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
                intentCreatorPackage, callingUid, callingPackage);
        Slog.wtf(TAG, msg);
        if (preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled(
                ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid)) {
            throw new SecurityException(msg, originalException);
        }
    }

    private static boolean logAndAbortForIntentRedirect(@NonNull String message,
            @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
            int callingUid, @Nullable String callingPackage) {
        String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid,
                intentCreatorPackage, callingUid, callingPackage);
        Slog.wtf(TAG, msg);
        return preventIntentRedirectAbortOrThrowException() && CompatChanges.isChangeEnabled(
                ENABLE_PREVENT_INTENT_REDIRECT_TAKE_ACTION, callingUid);
    }

    private static String getIntentRedirectPreventedLogMessage(String message, Intent intent,
            int intentCreatorUid, String intentCreatorPackage, int callingUid,
            String callingPackage) {
    private static String getIntentRedirectPreventedLogMessage(@NonNull String message,
            @NonNull Intent intent, int intentCreatorUid, @Nullable String intentCreatorPackage,
            int callingUid, @Nullable String callingPackage) {
        return "[IntentRedirect]" + message + " intentCreatorUid: " + intentCreatorUid
                + "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid
                + "; callingPackage: " + callingPackage + "; intent: " + intent;