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

Commit 6728d4f9 authored by Jeff Sharkey's avatar Jeff Sharkey Committed by Michael Groover
Browse files

Add StrictMode check for unsafe intent launching

Exported components that are not guarded by a signature permission
can receive Intents from any other app on a device. If an app unparcels
and launches an Intent from the Intent delivered to this unprotected
component then a malicious actor can potentially craft an Intent that
could launch hidden components, grant URI permissions, etc. This
commit adds a StrictMode check to report if a component launches an
Intent unparceled from the delivered Intent.

Bug: 160796858
Test: atest StrictModeTest
Change-Id: I763b8a965f91f5b433ce2f4b619e10ef12f5c296
parent fd763b65
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -31276,6 +31276,7 @@ package android.os {
    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedRegistrationObjects();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectLeakedSqlLiteObjects();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectNonSdkApiUsage();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUnsafeIntentLaunch();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder detectUntaggedSockets();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeath();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyDeathOnCleartextNetwork();
@@ -31284,6 +31285,7 @@ package android.os {
    method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyListener(@NonNull java.util.concurrent.Executor, @NonNull android.os.StrictMode.OnVmViolationListener);
    method @NonNull public android.os.StrictMode.VmPolicy.Builder penaltyLog();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder permitNonSdkApiUsage();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder permitUnsafeIntentLaunch();
    method @NonNull public android.os.StrictMode.VmPolicy.Builder setClassInstanceLimit(Class, int);
  }
@@ -31810,6 +31812,9 @@ package android.os.strictmode {
  public final class UnbufferedIoViolation extends android.os.strictmode.Violation {
  }
  public final class UnsafeIntentLaunchViolation extends android.os.strictmode.Violation {
  }
  public final class UntaggedSocketViolation extends android.os.strictmode.Violation {
  }
+76 −7
Original line number Diff line number Diff line
@@ -64,12 +64,14 @@ import android.content.IIntentReceiver;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.ComponentInfo;
import android.content.pm.IPackageManager;
import android.content.pm.InstrumentationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ParceledListSlice;
import android.content.pm.PermissionInfo;
import android.content.pm.ProviderInfo;
import android.content.pm.ProviderInfoList;
import android.content.pm.ServiceInfo;
@@ -346,6 +348,7 @@ public final class ActivityThread extends ClientTransactionHandler {
    private int mPendingProcessState = PROCESS_STATE_UNKNOWN;
    ArrayList<WeakReference<AssistStructure>> mLastAssistStructures = new ArrayList<>();
    private int mLastSessionId;
    final ArrayMap<IBinder, CreateServiceData> mServicesData = new ArrayMap<>();
    @UnsupportedAppUsage
    final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();
    @UnsupportedAppUsage
@@ -3412,7 +3415,7 @@ public final class ActivityThread extends ClientTransactionHandler {
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            r.intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo));
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
@@ -3717,7 +3720,7 @@ public final class ActivityThread extends ClientTransactionHandler {
        for (int i=0; i<N; i++) {
            ReferrerIntent intent = intents.get(i);
            intent.setExtrasClassLoader(r.activity.getClassLoader());
            intent.prepareToEnterProcess();
            intent.prepareToEnterProcess(isProtectedComponent(r.activityInfo));
            r.activity.mFragments.noteStateNotSaved();
            mInstrumentation.callActivityOnNewIntent(r.activity, intent);
        }
@@ -4052,7 +4055,8 @@ public final class ActivityThread extends ClientTransactionHandler {
            }
            java.lang.ClassLoader cl = context.getClassLoader();
            data.intent.setExtrasClassLoader(cl);
            data.intent.prepareToEnterProcess();
            data.intent.prepareToEnterProcess(
                    isProtectedComponent(data.info) || isProtectedBroadcast(data.intent));
            data.setExtrasClassLoader(cl);
            receiver = packageInfo.getAppFactory()
                    .instantiateReceiver(cl, data.info.name, data.intent);
@@ -4249,6 +4253,7 @@ public final class ActivityThread extends ClientTransactionHandler {
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            service.onCreate();
            mServicesData.put(data.token, data);
            mServices.put(data.token, service);
            try {
                ActivityManager.getService().serviceDoneExecuting(
@@ -4266,13 +4271,14 @@ public final class ActivityThread extends ClientTransactionHandler {
    }

    private void handleBindService(BindServiceData data) {
        CreateServiceData createData = mServicesData.get(data.token);
        Service s = mServices.get(data.token);
        if (DEBUG_SERVICE)
            Slog.v(TAG, "handleBindService s=" + s + " rebind=" + data.rebind);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                data.intent.prepareToEnterProcess(isProtectedComponent(createData.info));
                try {
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
@@ -4297,11 +4303,12 @@ public final class ActivityThread extends ClientTransactionHandler {
    }

    private void handleUnbindService(BindServiceData data) {
        CreateServiceData createData = mServicesData.get(data.token);
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                data.intent.setExtrasClassLoader(s.getClassLoader());
                data.intent.prepareToEnterProcess();
                data.intent.prepareToEnterProcess(isProtectedComponent(createData.info));
                boolean doRebind = s.onUnbind(data.intent);
                try {
                    if (doRebind) {
@@ -4373,12 +4380,13 @@ public final class ActivityThread extends ClientTransactionHandler {
    }

    private void handleServiceArgs(ServiceArgsData data) {
        CreateServiceData createData = mServicesData.get(data.token);
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                if (data.args != null) {
                    data.args.setExtrasClassLoader(s.getClassLoader());
                    data.args.prepareToEnterProcess();
                    data.args.prepareToEnterProcess(isProtectedComponent(createData.info));
                }
                int res;
                if (!data.taskRemoved) {
@@ -4407,6 +4415,7 @@ public final class ActivityThread extends ClientTransactionHandler {
    }

    private void handleStopService(IBinder token) {
        mServicesData.remove(token);
        Service s = mServices.remove(token);
        if (s != null) {
            try {
@@ -5026,7 +5035,7 @@ public final class ActivityThread extends ClientTransactionHandler {
            try {
                if (ri.mData != null) {
                    ri.mData.setExtrasClassLoader(r.activity.getClassLoader());
                    ri.mData.prepareToEnterProcess();
                    ri.mData.prepareToEnterProcess(isProtectedComponent(r.activityInfo));
                }
                if (DEBUG_RESULTS) Slog.v(TAG,
                        "Delivering result to activity " + r + " : " + ri);
@@ -7739,6 +7748,66 @@ public final class ActivityThread extends ClientTransactionHandler {
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    }

    /**
     * Returns whether the provided {@link ActivityInfo} {@code ai} is a protected component.
     *
     * @see #isProtectedComponent(ComponentInfo, String)
     */
    public static boolean isProtectedComponent(@NonNull ActivityInfo ai) {
        return isProtectedComponent(ai, ai.permission);
    }

    /**
     * Returns whether the provided {@link ServiceInfo} {@code si} is a protected component.
     *
     * @see #isProtectedComponent(ComponentInfo, String)
     */
    public static boolean isProtectedComponent(@NonNull ServiceInfo si) {
        return isProtectedComponent(si, si.permission);
    }

    /**
     * Returns whether the provided {@link ComponentInfo} {@code ci} with the specified {@code
     * permission} is a protected component.
     *
     * <p>A component is protected if it is not exported, or if the specified {@code permission} is
     * a signature permission.
     */
    private static boolean isProtectedComponent(@NonNull ComponentInfo ci,
            @Nullable String permission) {
        // Bail early when this process isn't looking for violations
        if (!StrictMode.vmUnsafeIntentLaunchEnabled()) return false;

        // TODO: consider optimizing by having AMS pre-calculate this value
        if (!ci.exported) {
            return true;
        }
        if (permission != null) {
            try {
                PermissionInfo pi = getPermissionManager().getPermissionInfo(permission,
                        currentOpPackageName(), 0);
                return (pi != null) && pi.getProtection() == PermissionInfo.PROTECTION_SIGNATURE;
            } catch (RemoteException ignored) {
            }
        }
        return false;
    }

    /**
     * Returns whether the action within the provided {@code intent} is a protected broadcast.
     */
    public static boolean isProtectedBroadcast(@NonNull Intent intent) {
        // Bail early when this process isn't looking for violations
        if (!StrictMode.vmUnsafeIntentLaunchEnabled()) return false;

        // TODO: consider optimizing by having AMS pre-calculate this value
        try {
            return getPackageManager().isProtectedBroadcast(intent.getAction());
        } catch (RemoteException ignored) {
        }
        return false;
    }

    // ------------------ Regular JNI ------------------------
    private native void nPurgePendingResources();
    private native void nDumpGraphicsInfo(FileDescriptor fd);
+3 −1
Original line number Diff line number Diff line
@@ -1670,7 +1670,9 @@ class ContextImpl extends Context {
                    flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
                // TODO: determine at registration time if caller is
                // protecting themselves with signature permission
                intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent));
            }
            return intent;
        } catch (RemoteException e) {
+3 −1
Original line number Diff line number Diff line
@@ -1617,7 +1617,9 @@ public final class LoadedApk {
                    try {
                        ClassLoader cl = mReceiver.getClass().getClassLoader();
                        intent.setExtrasClassLoader(cl);
                        intent.prepareToEnterProcess();
                        // TODO: determine at registration time if caller is
                        // protecting themselves with signature permission
                        intent.prepareToEnterProcess(ActivityThread.isProtectedBroadcast(intent));
                        setExtrasClassLoader(cl);
                        receiver.setPendingResult(this);
                        receiver.onReceive(mContext, intent);
+3 −1
Original line number Diff line number Diff line
@@ -1008,7 +1008,9 @@ public class ClipData implements Parcelable {
        for (int i = 0; i < size; i++) {
            final Item item = mItems.get(i);
            if (item.mIntent != null) {
                item.mIntent.prepareToEnterProcess();
                // We can't recursively claim that this data is from a protected
                // component, since it may have been filled in by a malicious app
                item.mIntent.prepareToEnterProcess(false);
            }
        }
    }
Loading