diff options
13 files changed, 374 insertions, 57 deletions
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java index 3bd121a4a19b..f80121d0c9b6 100644 --- a/core/java/android/app/ActivityManagerInternal.java +++ b/core/java/android/app/ActivityManagerInternal.java @@ -1328,7 +1328,8 @@ public abstract class ActivityManagerInternal { * Add a creator token for all embedded intents (stored as extra) of the given intent. * * @param intent The given intent + * @param creatorPackage the package name of the creator app. * @hide */ - public abstract void addCreatorToken(Intent intent); + public abstract void addCreatorToken(Intent intent, String creatorPackage); } diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 0bb0027fb0c3..f71952849872 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -888,6 +888,22 @@ public class Intent implements Parcelable, Cloneable { public static final String ACTION_ACTIVITY_RECOGNIZER = "android.intent.action.ACTIVITY_RECOGNIZER"; + /** @hide */ + public static void maybeMarkAsMissingCreatorToken(Object object) { + if (object instanceof Intent intent) { + maybeMarkAsMissingCreatorTokenInternal(intent); + } + } + + private static void maybeMarkAsMissingCreatorTokenInternal(Intent intent) { + boolean isForeign = (intent.mLocalFlags & LOCAL_FLAG_FROM_PARCEL) != 0; + boolean isWithoutTrustedCreatorToken = + (intent.mLocalFlags & Intent.LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT) == 0; + if (isForeign && isWithoutTrustedCreatorToken) { + intent.addExtendedFlags(EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN); + } + } + /** * Represents a shortcut/live folder icon resource. * @@ -7684,10 +7700,8 @@ public class Intent implements Parcelable, Cloneable { /** * This flag indicates the creator token of this intent has been verified. - * - * @hide */ - public static final int LOCAL_FLAG_CREATOR_TOKEN_VERIFIED = 1 << 6; + private static final int LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT = 1 << 6; /** @hide */ @IntDef(flag = true, prefix = { "EXTENDED_FLAG_" }, value = { @@ -12243,6 +12257,30 @@ public class Intent implements Parcelable, Cloneable { } } + /** @hide */ + public void checkCreatorToken() { + if (mExtras == null) return; + if (mCreatorTokenInfo != null && mCreatorTokenInfo.mExtraIntentKeys != null) { + for (String key : mCreatorTokenInfo.mExtraIntentKeys) { + try { + Intent extraIntent = mExtras.getParcelable(key, Intent.class); + if (extraIntent == null) { + Log.w(TAG, "The key {" + key + + "} does not correspond to an intent in the bundle."); + continue; + } + extraIntent.mLocalFlags |= LOCAL_FLAG_TRUSTED_CREATOR_TOKEN_PRESENT; + } catch (Exception e) { + Log.e(TAG, "Failed to validate creator token. key: " + key + ".", e); + } + } + } + // mark the bundle as intent extras after calls to getParcelable. + // otherwise, the logic to mark missing token would run before + // mark trusted creator token present. + mExtras.setIsIntentExtra(); + } + public void writeToParcel(Parcel out, int flags) { out.writeString8(mAction); Uri.writeToParcel(out, mData); @@ -12730,6 +12768,7 @@ public class Intent implements Parcelable, Cloneable { } mLocalFlags |= localFlags; + checkCreatorToken(); // Special attribution fix-up logic for any BluetoothDevice extras // passed via Bluetooth intents diff --git a/core/java/android/os/BaseBundle.java b/core/java/android/os/BaseBundle.java index 49ab15a40a8e..50121278f0e6 100644 --- a/core/java/android/os/BaseBundle.java +++ b/core/java/android/os/BaseBundle.java @@ -21,6 +21,7 @@ import static java.util.Objects.requireNonNull; import android.annotation.NonNull; import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; +import android.content.Intent; import android.util.ArrayMap; import android.util.Log; import android.util.MathUtils; @@ -401,6 +402,9 @@ public class BaseBundle { synchronized (this) { object = unwrapLazyValueFromMapLocked(i, clazz, itemTypes); } + if ((mFlags & Bundle.FLAG_VERIFY_TOKENS_PRESENT) != 0) { + Intent.maybeMarkAsMissingCreatorToken(object); + } } return (clazz != null) ? clazz.cast(object) : (T) object; } diff --git a/core/java/android/os/Bundle.java b/core/java/android/os/Bundle.java index ed4037c7d246..c18fb0c38b82 100644 --- a/core/java/android/os/Bundle.java +++ b/core/java/android/os/Bundle.java @@ -62,6 +62,12 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { @VisibleForTesting static final int FLAG_HAS_BINDERS = 1 << 12; + /** + * Indicates there may be intents with creator tokens contained in this bundle. When unparceled, + * they should be verified if tokens are missing or invalid. + */ + static final int FLAG_VERIFY_TOKENS_PRESENT = 1 << 13; + /** * Status when the Bundle can <b>assert</b> that the underlying Parcel DOES NOT contain @@ -274,6 +280,11 @@ public final class Bundle extends BaseBundle implements Cloneable, Parcelable { return orig; } + /** {@hide} */ + public void setIsIntentExtra() { + mFlags |= FLAG_VERIFY_TOKENS_PRESENT; + } + /** * Mark if this Bundle is okay to "defuse." That is, it's okay for system * processes to ignore any {@link BadParcelableException} encountered when diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 3c95b9a02557..33a8fa58360d 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -5536,7 +5536,6 @@ public class ActivityManagerService extends IActivityManager.Stub public int sendIntentSender(IApplicationThread caller, IIntentSender target, IBinder allowlistToken, int code, Intent intent, String resolvedType, IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) { - addCreatorToken(intent); if (target instanceof PendingIntentRecord) { final PendingIntentRecord originalRecord = (PendingIntentRecord) target; @@ -5578,19 +5577,23 @@ public class ActivityManagerService extends IActivityManager.Stub intent = new Intent(Intent.ACTION_MAIN); } try { + final int callingUid = Binder.getCallingUid(); + final String packageName; + final long token = Binder.clearCallingIdentity(); + try { + packageName = AppGlobals.getPackageManager().getNameForUid(callingUid); + } finally { + Binder.restoreCallingIdentity(token); + } + if (allowlistToken != null) { - final int callingUid = Binder.getCallingUid(); - final String packageName; - final long token = Binder.clearCallingIdentity(); - try { - packageName = AppGlobals.getPackageManager().getNameForUid(callingUid); - } finally { - Binder.restoreCallingIdentity(token); - } Slog.wtf(TAG, "Send a non-null allowlistToken to a non-PI target." + " Calling package: " + packageName + "; intent: " + intent + "; options: " + options); } + + addCreatorToken(intent, packageName); + target.send(code, intent, resolvedType, null, null, requiredPermission, options); } catch (RemoteException e) { @@ -13615,7 +13618,7 @@ public class ActivityManagerService extends IActivityManager.Stub throws TransactionTooLargeException { enforceNotIsolatedCaller("startService"); enforceAllowedToStartOrBindServiceIfSdkSandbox(service); - addCreatorToken(service); + addCreatorToken(service, callingPackage); if (service != null) { // Refuse possible leaked file descriptors if (service.hasFileDescriptors()) { @@ -13877,7 +13880,7 @@ public class ActivityManagerService extends IActivityManager.Stub validateServiceInstanceName(instanceName); - addCreatorToken(service); + addCreatorToken(service, callingPackage); try { if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) { final ComponentName cn = service.getComponent(); @@ -17155,7 +17158,7 @@ public class ActivityManagerService extends IActivityManager.Stub Slog.v(TAG_SERVICE, "startServiceInPackage: " + service + " type=" + resolvedType); } - addCreatorToken(service); + addCreatorToken(service, callingPackage); final long origId = Binder.clearCallingIdentity(); ComponentName res; try { @@ -17983,8 +17986,8 @@ public class ActivityManagerService extends IActivityManager.Stub } @Override - public void addCreatorToken(Intent intent) { - ActivityManagerService.this.addCreatorToken(intent); + public void addCreatorToken(Intent intent, String creatorPackage) { + ActivityManagerService.this.addCreatorToken(intent, creatorPackage); } } @@ -19141,9 +19144,9 @@ public class ActivityManagerService extends IActivityManager.Stub private final Key mKeyFields; private final WeakReference<IntentCreatorToken> mRef; - public IntentCreatorToken(int creatorUid, Intent intent) { + public IntentCreatorToken(int creatorUid, String creatorPackage, Intent intent) { super(); - this.mKeyFields = new Key(creatorUid, intent); + this.mKeyFields = new Key(creatorUid, creatorPackage, intent); mRef = new WeakReference<>(this); } @@ -19151,7 +19154,10 @@ public class ActivityManagerService extends IActivityManager.Stub return mKeyFields.mCreatorUid; } - /** {@hide} */ + public String getCreatorPackage() { + return mKeyFields.mCreatorPackage; + } + public static boolean isValid(@NonNull Intent intent) { IBinder binder = intent.getCreatorToken(); IntentCreatorToken token = null; @@ -19159,7 +19165,8 @@ public class ActivityManagerService extends IActivityManager.Stub token = (IntentCreatorToken) binder; } return token != null && token.mKeyFields.equals( - new Key(token.mKeyFields.mCreatorUid, intent)); + new Key(token.mKeyFields.mCreatorUid, token.mKeyFields.mCreatorPackage, + intent)); } @Override @@ -19183,8 +19190,9 @@ public class ActivityManagerService extends IActivityManager.Stub } private static class Key { - private Key(int creatorUid, Intent intent) { + private Key(int creatorUid, String creatorPackage, Intent intent) { this.mCreatorUid = creatorUid; + this.mCreatorPackage = creatorPackage; this.mAction = intent.getAction(); this.mData = intent.getData(); this.mType = intent.getType(); @@ -19201,6 +19209,7 @@ public class ActivityManagerService extends IActivityManager.Stub } private final int mCreatorUid; + private final String mCreatorPackage; private final String mAction; private final Uri mData; private final String mType; @@ -19214,17 +19223,20 @@ public class ActivityManagerService extends IActivityManager.Stub if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Key key = (Key) o; - return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags && Objects.equals( - mAction, key.mAction) && Objects.equals(mData, key.mData) - && Objects.equals(mType, key.mType) && Objects.equals(mPackage, - key.mPackage) && Objects.equals(mComponent, key.mComponent) + return mCreatorUid == key.mCreatorUid && mFlags == key.mFlags + && Objects.equals(mCreatorPackage, key.mCreatorPackage) + && Objects.equals(mAction, key.mAction) + && Objects.equals(mData, key.mData) + && Objects.equals(mType, key.mType) + && Objects.equals(mPackage, key.mPackage) + && Objects.equals(mComponent, key.mComponent) && Objects.equals(mClipDataUris, key.mClipDataUris); } @Override public int hashCode() { - return Objects.hash(mCreatorUid, mAction, mData, mType, mPackage, mComponent, - mFlags, mClipDataUris); + return Objects.hash(mCreatorUid, mCreatorPackage, mAction, mData, mType, mPackage, + mComponent, mFlags, mClipDataUris); } } } @@ -19235,7 +19247,7 @@ public class ActivityManagerService extends IActivityManager.Stub * @param intent The given intent * @hide */ - public void addCreatorToken(@Nullable Intent intent) { + public void addCreatorToken(@Nullable Intent intent, String creatorPackage) { if (!preventIntentRedirect()) return; if (intent == null || intent.getExtraIntentKeys() == null) return; @@ -19248,7 +19260,7 @@ public class ActivityManagerService extends IActivityManager.Stub continue; } Slog.wtf(TAG, "A creator token is added to an intent."); - IBinder creatorToken = createIntentCreatorToken(extraIntent); + IBinder creatorToken = createIntentCreatorToken(extraIntent, creatorPackage); if (creatorToken != null) { extraIntent.setCreatorToken(creatorToken); } @@ -19261,15 +19273,15 @@ public class ActivityManagerService extends IActivityManager.Stub } } - private IBinder createIntentCreatorToken(Intent intent) { + private IBinder createIntentCreatorToken(Intent intent, String creatorPackage) { if (IntentCreatorToken.isValid(intent)) return null; int creatorUid = getCallingUid(); - IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, intent); + IntentCreatorToken.Key key = new IntentCreatorToken.Key(creatorUid, creatorPackage, intent); IntentCreatorToken token; synchronized (sIntentCreatorTokenCache) { WeakReference<IntentCreatorToken> ref = sIntentCreatorTokenCache.get(key); if (ref == null || ref.get() == null) { - token = new IntentCreatorToken(creatorUid, intent); + token = new IntentCreatorToken(creatorUid, creatorPackage, intent); sIntentCreatorTokenCache.put(key, token.mRef); } else { token = ref.get(); diff --git a/services/core/java/com/android/server/am/PendingIntentRecord.java b/services/core/java/com/android/server/am/PendingIntentRecord.java index 6857b6bcde15..3fb06a75d79f 100644 --- a/services/core/java/com/android/server/am/PendingIntentRecord.java +++ b/services/core/java/com/android/server/am/PendingIntentRecord.java @@ -432,6 +432,14 @@ public final class PendingIntentRecord extends IIntentSender.Stub { } } + /** + * get package name of the PendingIntent sender. + * @return package name of the PendingIntent sender. + */ + public String getPackageName() { + return key.packageName; + } + @Deprecated public int sendInner(int code, Intent intent, String resolvedType, IBinder allowlistToken, IIntentReceiver finishedReceiver, String requiredPermission, IBinder resultTo, diff --git a/services/core/java/com/android/server/uri/NeededUriGrants.java b/services/core/java/com/android/server/uri/NeededUriGrants.java index 8c8f55304fbb..2fe61e00c97e 100644 --- a/services/core/java/com/android/server/uri/NeededUriGrants.java +++ b/services/core/java/com/android/server/uri/NeededUriGrants.java @@ -17,10 +17,13 @@ package com.android.server.uri; import android.util.ArraySet; +import android.util.Slog; import android.util.proto.ProtoOutputStream; import com.android.server.am.NeededUriGrantsProto; +import java.util.Objects; + /** List of {@link GrantUri} a process needs. */ public class NeededUriGrants { final String targetPkg; @@ -35,6 +38,20 @@ public class NeededUriGrants { this.uris = new ArraySet<>(); } + public void merge(NeededUriGrants other) { + if (other == null) return; + if (!Objects.equals(this.targetPkg, other.targetPkg) + || this.targetUid != other.targetUid || this.flags != other.flags) { + Slog.wtf("NeededUriGrants", + "The other NeededUriGrants does not share the same targetUid, targetPkg or " + + "flags. It cannot be merged into this NeededUriGrants. This " + + "NeededUriGrants: " + this.toStringWithoutUri() + + ". Other NeededUriGrants: " + other.toStringWithoutUri()); + } else { + this.uris.addAll(other.uris); + } + } + public void dumpDebug(ProtoOutputStream proto, long fieldId) { long token = proto.start(fieldId); proto.write(NeededUriGrantsProto.TARGET_PACKAGE, targetPkg); @@ -47,4 +64,12 @@ public class NeededUriGrants { } proto.end(token); } + + public String toStringWithoutUri() { + return "NeededUriGrants{" + + "targetPkg='" + targetPkg + '\'' + + ", targetUid=" + targetUid + + ", flags=" + flags + + '}'; + } } diff --git a/services/core/java/com/android/server/wm/ActivityClientController.java b/services/core/java/com/android/server/wm/ActivityClientController.java index ae30fcde39e1..d119a08b0c85 100644 --- a/services/core/java/com/android/server/wm/ActivityClientController.java +++ b/services/core/java/com/android/server/wm/ActivityClientController.java @@ -442,8 +442,6 @@ class ActivityClientController extends IActivityClientController.Stub { throw new IllegalArgumentException("File descriptors passed in Intent"); } - mService.mAmInternal.addCreatorToken(resultData); - final ActivityRecord r; synchronized (mGlobalLock) { r = ActivityRecord.isInRootTaskLocked(token); @@ -502,6 +500,8 @@ class ActivityClientController extends IActivityClientController.Stub { r.app.setLastActivityFinishTimeIfNeeded(SystemClock.uptimeMillis()); } + mService.mAmInternal.addCreatorToken(resultData, r.packageName); + final long origId = Binder.clearCallingIdentity(); Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "finishActivity"); try { diff --git a/services/core/java/com/android/server/wm/ActivityStartController.java b/services/core/java/com/android/server/wm/ActivityStartController.java index 35ec5adf54b0..f71fe1973aca 100644 --- a/services/core/java/com/android/server/wm/ActivityStartController.java +++ b/services/core/java/com/android/server/wm/ActivityStartController.java @@ -25,6 +25,7 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.FactoryTest.FACTORY_TEST_LOW_LEVEL; +import static com.android.server.wm.ActivityStarter.Request.DEFAULT_INTENT_CREATOR_UID; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_ATM; import static com.android.server.wm.ActivityTaskManagerDebugConfig.TAG_WITH_CLASS_NAME; import static com.android.server.wm.ActivityTaskSupervisor.ON_TOP; @@ -442,6 +443,17 @@ public class ActivityStartController { 0 /* startFlags */, null /* profilerInfo */, userId, filterCallingUid, callingPid); aInfo = mService.mAmInternal.getActivityInfoForUser(aInfo, userId); + int creatorUid = DEFAULT_INTENT_CREATOR_UID; + String creatorPackage = null; + if (ActivityManagerService.IntentCreatorToken.isValid(intent)) { + ActivityManagerService.IntentCreatorToken creatorToken = + (ActivityManagerService.IntentCreatorToken) intent.getCreatorToken(); + if (creatorToken.getCreatorUid() != filterCallingUid) { + creatorUid = creatorToken.getCreatorUid(); + creatorPackage = creatorToken.getCreatorPackage(); + } + // leave creatorUid as -1 if the intent creator is the same as the launcher + } if (aInfo != null) { try { @@ -455,6 +467,24 @@ public class ActivityStartController { return START_CANCELED; } + if (creatorUid != DEFAULT_INTENT_CREATOR_UID) { + try { + NeededUriGrants creatorIntentGrants = mSupervisor.mService.mUgmInternal + .checkGrantUriPermissionFromIntent(intent, creatorUid, + aInfo.applicationInfo.packageName, + UserHandle.getUserId(aInfo.applicationInfo.uid)); + if (intentGrants == null) { + intentGrants = creatorIntentGrants; + } else { + intentGrants.merge(creatorIntentGrants); + } + } catch (SecurityException securityException) { + ActivityStarter.logForIntentRedirect( + "Creator URI Grant Caused Exception.", intent, creatorUid, + creatorPackage, filterCallingUid, callingPackage); + // TODO b/368559093 - rethrow the securityException. + } + } if ((aInfo.applicationInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE) != 0) { throw new IllegalArgumentException( @@ -478,6 +508,8 @@ public class ActivityStartController { .setCallingUid(callingUid) .setCallingPackage(callingPackage) .setCallingFeatureId(callingFeatureId) + .setIntentCreatorUid(creatorUid) + .setIntentCreatorPackage(creatorPackage) .setRealCallingPid(realCallingPid) .setRealCallingUid(realCallingUid) .setActivityOptions(checkedOptions) diff --git a/services/core/java/com/android/server/wm/ActivityStarter.java b/services/core/java/com/android/server/wm/ActivityStarter.java index 87fa62ac0e3b..7e6abe121dd3 100644 --- a/services/core/java/com/android/server/wm/ActivityStarter.java +++ b/services/core/java/com/android/server/wm/ActivityStarter.java @@ -131,6 +131,7 @@ import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.HeavyWeightSwitcherActivity; import com.android.internal.app.IVoiceInteractor; import com.android.internal.protolog.ProtoLog; +import com.android.server.am.ActivityManagerService.IntentCreatorToken; import com.android.server.am.PendingIntentRecord; import com.android.server.pm.InstantAppResolver; import com.android.server.pm.PackageArchiver; @@ -383,6 +384,7 @@ class ActivityStarter { private static final int DEFAULT_CALLING_PID = 0; static final int DEFAULT_REAL_CALLING_UID = -1; static final int DEFAULT_REAL_CALLING_PID = 0; + static final int DEFAULT_INTENT_CREATOR_UID = -1; IApplicationThread caller; Intent intent; @@ -403,6 +405,8 @@ class ActivityStarter { @Nullable String callingFeatureId; int realCallingPid = DEFAULT_REAL_CALLING_PID; int realCallingUid = DEFAULT_REAL_CALLING_UID; + int intentCreatorUid = DEFAULT_INTENT_CREATOR_UID; + String intentCreatorPackage; int startFlags; SafeActivityOptions activityOptions; boolean ignoreTargetSecurity; @@ -463,6 +467,8 @@ class ActivityStarter { callingPid = DEFAULT_CALLING_PID; callingUid = DEFAULT_CALLING_UID; callingPackage = null; + intentCreatorUid = DEFAULT_INTENT_CREATOR_UID; + intentCreatorPackage = null; callingFeatureId = null; realCallingPid = DEFAULT_REAL_CALLING_PID; realCallingUid = DEFAULT_REAL_CALLING_UID; @@ -555,12 +561,14 @@ class ActivityStarter { // "resolved" calling UID, where we try our best to identify the // actual caller that is starting this activity int resolvedCallingUid = callingUid; + String resolvedCallingPackage = callingPackage; if (caller != null) { synchronized (supervisor.mService.mGlobalLock) { final WindowProcessController callerApp = supervisor.mService .getProcessController(caller); if (callerApp != null) { resolvedCallingUid = callerApp.mInfo.uid; + resolvedCallingPackage = callerApp.mInfo.packageName; } } } @@ -596,7 +604,23 @@ class ActivityStarter { // Collect information about the target of the Intent. activityInfo = supervisor.resolveActivity(intent, resolveInfo, startFlags, profilerInfo); - + // Check if the Intent was redirected + if ((intent.getExtendedFlags() & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN) + != 0) { + ActivityStarter.logForIntentRedirect( + "Unparceled intent does not have a creator token set.", intent, + intentCreatorUid, + intentCreatorPackage, resolvedCallingUid, resolvedCallingPackage); + // TODO b/368559093 - eventually ramp up to throw SecurityException + } + if (IntentCreatorToken.isValid(intent)) { + IntentCreatorToken creatorToken = (IntentCreatorToken) intent.getCreatorToken(); + if (creatorToken.getCreatorUid() != resolvedCallingUid) { + intentCreatorUid = creatorToken.getCreatorUid(); + intentCreatorPackage = creatorToken.getCreatorPackage(); + } + // leave intentCreatorUid as -1 if the intent creator is the same as the launcher + } // Carefully collect grants without holding lock if (activityInfo != null) { if (android.security.Flags.contentUriPermissionApis()) { @@ -606,11 +630,52 @@ class ActivityStarter { UserHandle.getUserId(activityInfo.applicationInfo.uid), activityInfo.requireContentUriPermissionFromCaller, /* requestHashCode */ this.hashCode()); + if (intentCreatorUid != DEFAULT_INTENT_CREATOR_UID) { + try { + NeededUriGrants creatorIntentGrants = supervisor.mService.mUgmInternal + .checkGrantUriPermissionFromIntent(intent, intentCreatorUid, + activityInfo.applicationInfo.packageName, + UserHandle.getUserId(activityInfo.applicationInfo.uid), + activityInfo.requireContentUriPermissionFromCaller, + /* requestHashCode */ this.hashCode()); + if (intentGrants == null) { + intentGrants = creatorIntentGrants; + } else { + intentGrants.merge(creatorIntentGrants); + } + } catch (SecurityException securityException) { + ActivityStarter.logForIntentRedirect( + "Creator URI Grant Caused Exception.", intent, intentCreatorUid, + intentCreatorPackage, resolvedCallingUid, + resolvedCallingPackage); + // TODO b/368559093 - rethrow the securityException. + } + } } else { intentGrants = supervisor.mService.mUgmInternal .checkGrantUriPermissionFromIntent(intent, resolvedCallingUid, activityInfo.applicationInfo.packageName, UserHandle.getUserId(activityInfo.applicationInfo.uid)); + if (intentCreatorUid != DEFAULT_INTENT_CREATOR_UID && intentGrants != null) { + try { + NeededUriGrants creatorIntentGrants = supervisor.mService.mUgmInternal + .checkGrantUriPermissionFromIntent(intent, intentCreatorUid, + activityInfo.applicationInfo.packageName, + UserHandle.getUserId( + activityInfo.applicationInfo.uid)); + if (intentGrants == null) { + intentGrants = creatorIntentGrants; + } else { + intentGrants.merge(creatorIntentGrants); + } + } catch (SecurityException securityException) { + ActivityStarter.logForIntentRedirect( + "Creator URI Grant Caused Exception.", intent, intentCreatorUid, + intentCreatorPackage, resolvedCallingUid, + resolvedCallingPackage); + // TODO b/368559093 - rethrow the securityException. + } + } } } } @@ -977,7 +1042,9 @@ class ActivityStarter { int requestCode = request.requestCode; int callingPid = request.callingPid; int callingUid = request.callingUid; - String callingPackage = request.callingPackage; + int intentCreatorUid = request.intentCreatorUid; + String intentCreatorPackage = request.intentCreatorPackage; + String intentCallingPackage = request.callingPackage; String callingFeatureId = request.callingFeatureId; final int realCallingPid = request.realCallingPid; final int realCallingUid = request.realCallingUid; @@ -1062,7 +1129,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. - callingPackage = sourceRecord.launchedFromPackage; + intentCallingPackage = sourceRecord.launchedFromPackage; callingFeatureId = sourceRecord.launchedFromFeatureId; } } @@ -1084,7 +1151,7 @@ class ActivityStarter { if (packageArchiver.isIntentResolvedToArchivedApp(intent, mRequest.userId)) { err = packageArchiver .requestUnarchiveOnActivityStart( - intent, callingPackage, mRequest.userId, realCallingUid); + intent, intentCallingPackage, mRequest.userId, realCallingUid); } } } @@ -1143,7 +1210,7 @@ class ActivityStarter { boolean abort; try { abort = !mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, - requestCode, callingPid, callingUid, callingPackage, callingFeatureId, + requestCode, callingPid, callingUid, intentCallingPackage, callingFeatureId, request.ignoreTargetSecurity, inTask != null, callerApp, resultRecord, resultRootTask); } catch (SecurityException e) { @@ -1171,7 +1238,47 @@ class ActivityStarter { abort |= !mService.mIntentFirewall.checkStartActivity(intent, callingUid, callingPid, resolvedType, aInfo.applicationInfo); abort |= !mService.getPermissionPolicyInternal().checkStartActivity(intent, callingUid, - callingPackage); + intentCallingPackage); + + if (intentCreatorUid != Request.DEFAULT_INTENT_CREATOR_UID) { + try { + if (!mSupervisor.checkStartAnyActivityPermission(intent, aInfo, resultWho, + requestCode, 0, intentCreatorUid, intentCreatorPackage, "", + request.ignoreTargetSecurity, inTask != null, null, resultRecord, + resultRootTask)) { + logForIntentRedirect("Creator checkStartAnyActivityPermission Caused abortion.", + intent, intentCreatorUid, intentCreatorPackage, callingUid, + intentCallingPackage); + // TODO b/368559093 - set abort to true. + // abort = true; + } + } catch (SecurityException e) { + logForIntentRedirect("Creator checkStartAnyActivityPermission Caused Exception.", + intent, intentCreatorUid, intentCreatorPackage, callingUid, + intentCallingPackage); + // TODO b/368559093 - rethrow the exception. + //throw 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.getPermissionPolicyInternal().checkStartActivity(intent, intentCreatorUid, + intentCreatorPackage)) { + logForIntentRedirect( + "Creator PermissionPolicyService.checkStartActivity Caused abortion.", + intent, intentCreatorUid, intentCreatorPackage, callingUid, + intentCallingPackage); + // TODO b/368559093 - set abort to true. + // abort = true; + } + intent.removeCreatorTokenInfo(); + } // Merge the two options bundles, while realCallerOptions takes precedence. ActivityOptions checkedOptions = options != null @@ -1188,7 +1295,7 @@ class ActivityStarter { balController.checkBackgroundActivityStart( callingUid, callingPid, - callingPackage, + intentCallingPackage, realCallingUid, realCallingPid, callerApp, @@ -1209,7 +1316,7 @@ class ActivityStarter { if (request.allowPendingRemoteAnimationRegistryLookup) { checkedOptions = mService.getActivityStartController() .getPendingRemoteAnimationRegistry() - .overrideOptionsIfNeeded(callingPackage, checkedOptions); + .overrideOptionsIfNeeded(intentCallingPackage, checkedOptions); } if (mService.mController != null) { try { @@ -1225,7 +1332,8 @@ class ActivityStarter { final TaskDisplayArea suggestedLaunchDisplayArea = computeSuggestedLaunchDisplayArea(inTask, sourceRecord, checkedOptions); - mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, callingPackage, + mInterceptor.setStates(userId, realCallingPid, realCallingUid, startFlags, + intentCallingPackage, callingFeatureId); if (mInterceptor.intercept(intent, rInfo, aInfo, resolvedType, inTask, inTaskFragment, callingPid, callingUid, checkedOptions, suggestedLaunchDisplayArea)) { @@ -1263,7 +1371,8 @@ class ActivityStarter { if (mService.getPackageManagerInternalLocked().isPermissionsReviewRequired( aInfo.packageName, userId)) { final IIntentSender target = mService.getIntentSenderLocked( - ActivityManager.INTENT_SENDER_ACTIVITY, callingPackage, callingFeatureId, + ActivityManager.INTENT_SENDER_ACTIVITY, intentCallingPackage, + callingFeatureId, callingUid, userId, null, null, 0, new Intent[]{intent}, new String[]{resolvedType}, PendingIntent.FLAG_CANCEL_CURRENT | PendingIntent.FLAG_ONE_SHOT, null); @@ -1326,7 +1435,8 @@ class ActivityStarter { // app [on install success]. if (rInfo != null && rInfo.auxiliaryInfo != null) { intent = createLaunchIntent(rInfo.auxiliaryInfo, request.ephemeralIntent, - callingPackage, callingFeatureId, verificationBundle, resolvedType, userId); + intentCallingPackage, callingFeatureId, verificationBundle, resolvedType, + userId); resolvedType = null; callingUid = realCallingUid; callingPid = realCallingPid; @@ -1349,7 +1459,7 @@ class ActivityStarter { .setCaller(callerApp) .setLaunchedFromPid(callingPid) .setLaunchedFromUid(callingUid) - .setLaunchedFromPackage(callingPackage) + .setLaunchedFromPackage(intentCallingPackage) .setLaunchedFromFeature(callingFeatureId) .setIntent(intent) .setResolvedType(resolvedType) @@ -3295,6 +3405,16 @@ class ActivityStarter { return this; } + ActivityStarter setIntentCreatorUid(int uid) { + mRequest.intentCreatorUid = uid; + return this; + } + + ActivityStarter setIntentCreatorPackage(String intentCreatorPackage) { + mRequest.intentCreatorPackage = intentCreatorPackage; + return this; + } + /** * Sets the pid of the caller who requested to launch the activity. * @@ -3454,4 +3574,19 @@ class ActivityStarter { pw.print(" mInTaskFragment="); pw.println(mInTaskFragment); } + + static void logForIntentRedirect(String message, Intent intent, int intentCreatorUid, + String intentCreatorPackage, int callingUid, String callingPackage) { + String msg = getIntentRedirectPreventedLogMessage(message, intent, intentCreatorUid, + intentCreatorPackage, callingUid, callingPackage); + Slog.wtf(TAG, msg); + } + + private static String getIntentRedirectPreventedLogMessage(String message, Intent intent, + int intentCreatorUid, String intentCreatorPackage, int callingUid, + String callingPackage) { + return "[IntentRedirect]" + message + " intentCreatorUid: " + intentCreatorUid + + "; intentCreatorPackage: " + intentCreatorPackage + "; callingUid: " + callingUid + + "; callingPackage: " + callingPackage + "; intent: " + intent; + } } diff --git a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java index 3dfc8f4e5bf9..de6c93ff363b 100644 --- a/services/core/java/com/android/server/wm/ActivityTaskManagerService.java +++ b/services/core/java/com/android/server/wm/ActivityTaskManagerService.java @@ -1228,7 +1228,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) { - mAmInternal.addCreatorToken(intent); + mAmInternal.addCreatorToken(intent, callingPackage); return startActivityAsUser(caller, callingPackage, callingFeatureId, intent, resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions, UserHandle.getCallingUserId()); @@ -1243,7 +1243,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { enforceNotIsolatedCaller(reason); if (intents != null) { for (Intent intent : intents) { - mAmInternal.addCreatorToken(intent); + mAmInternal.addCreatorToken(intent, callingPackage); } } userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId, reason); @@ -1275,7 +1275,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { @Nullable String callingFeatureId, Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode, int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) { - mAmInternal.addCreatorToken(intent); + mAmInternal.addCreatorToken(intent, callingPackage); final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions); assertPackageMatchesCallingUid(callingPackage); @@ -1330,7 +1330,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { } // Remove existing mismatch flag so it can be properly updated later fillInIntent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); - mAmInternal.addCreatorToken(fillInIntent); } if (!(target instanceof PendingIntentRecord)) { @@ -1339,6 +1338,10 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { PendingIntentRecord pir = (PendingIntentRecord) target; + if (fillInIntent != null) { + mAmInternal.addCreatorToken(fillInIntent, pir.getPackageName()); + } + synchronized (mGlobalLock) { // If this is coming from the currently resumed activity, it is // effectively saying that app switches are allowed at this point. @@ -1349,6 +1352,7 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { mAppSwitchesState = APP_SWITCH_ALLOW; } } + return pir.sendInner(caller, 0, fillInIntent, resolvedType, allowlistToken, null, null, resultTo, resultWho, requestCode, flagsMask, flagsValues, bOptions); } @@ -1361,8 +1365,6 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { throw new IllegalArgumentException("File descriptors passed in Intent"); } - mAmInternal.addCreatorToken(intent); - SafeActivityOptions options = SafeActivityOptions.fromBundle(bOptions); synchronized (mGlobalLock) { @@ -1376,6 +1378,9 @@ public class ActivityTaskManagerService extends IActivityTaskManager.Stub { SafeActivityOptions.abort(options); return false; } + + mAmInternal.addCreatorToken(intent, r.packageName); + intent = new Intent(intent); // Remove existing mismatch flag so it can be properly updated later intent.removeExtendedFlags(Intent.EXTENDED_FLAG_FILTER_MISMATCH); diff --git a/services/core/java/com/android/server/wm/AppTaskImpl.java b/services/core/java/com/android/server/wm/AppTaskImpl.java index 4d17ed24e734..eee4c86bc483 100644 --- a/services/core/java/com/android/server/wm/AppTaskImpl.java +++ b/services/core/java/com/android/server/wm/AppTaskImpl.java @@ -160,7 +160,7 @@ class AppTaskImpl extends IAppTask.Stub { Intent intent, String resolvedType, Bundle bOptions) { checkCallerOrSystemOrRoot(); mService.assertPackageMatchesCallingUid(callingPackage); - mService.mAmInternal.addCreatorToken(intent); + mService.mAmInternal.addCreatorToken(intent, callingPackage); int callingUser = UserHandle.getCallingUserId(); Task task; diff --git a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java index 6ccc03709b4f..2a825f35bf62 100644 --- a/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java +++ b/services/tests/mockingservicestests/src/com/android/server/am/ActivityManagerServiceTest.java @@ -107,6 +107,7 @@ import android.os.IBinder; import android.os.IProgressListener; import android.os.Looper; import android.os.Message; +import android.os.Parcel; import android.os.Process; import android.os.RemoteException; import android.os.SystemClock; @@ -1309,12 +1310,13 @@ public class ActivityManagerServiceTest { intent.putExtra("EXTRA_INTENT0", extraIntent); intent.collectExtraIntentKeys(); - mAms.addCreatorToken(intent); + mAms.addCreatorToken(intent, TEST_PACKAGE); ActivityManagerService.IntentCreatorToken token = (ActivityManagerService.IntentCreatorToken) extraIntent.getCreatorToken(); assertThat(token).isNotNull(); assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid()); + assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE); } @Test @@ -1330,7 +1332,7 @@ public class ActivityManagerServiceTest { fillinIntent.collectExtraIntentKeys(); intent.fillIn(fillinIntent, FILL_IN_ACTION); - mAms.addCreatorToken(fillinIntent); + mAms.addCreatorToken(fillinIntent, TEST_PACKAGE); fillinExtraIntent = intent.getParcelableExtra("FILLIN_EXTRA_INTENT0", Intent.class); @@ -1338,6 +1340,49 @@ public class ActivityManagerServiceTest { (ActivityManagerService.IntentCreatorToken) fillinExtraIntent.getCreatorToken(); assertThat(token).isNotNull(); assertThat(token.getCreatorUid()).isEqualTo(mInjector.getCallingUid()); + assertThat(token.getCreatorPackage()).isEqualTo(TEST_PACKAGE); + } + + @Test + @RequiresFlagsEnabled(android.security.Flags.FLAG_PREVENT_INTENT_REDIRECT) + public void testCheckCreatorToken() { + Intent intent = new Intent(); + Intent extraIntent = new Intent("EXTRA_INTENT_ACTION"); + intent.putExtra("EXTRA_INTENT", extraIntent); + + intent.collectExtraIntentKeys(); + + // mimic client hack and sneak in an extra intent without going thru collectExtraIntentKeys. + Intent extraIntent2 = new Intent("EXTRA_INTENT_ACTION2"); + intent.putExtra("EXTRA_INTENT2", extraIntent2); + + // mock parceling on the client side, unparcling on the system server side, then + // addCreatorToken on system server side. + final Parcel parcel = Parcel.obtain(); + intent.writeToParcel(parcel, 0); + parcel.setDataPosition(0); + Intent newIntent = new Intent(); + newIntent.readFromParcel(parcel); + intent = newIntent; + mAms.addCreatorToken(intent, TEST_PACKAGE); + // entering the target app's process. + intent.checkCreatorToken(); + + Intent extraIntent3 = new Intent("EXTRA_INTENT_ACTION3"); + intent.putExtra("EXTRA_INTENT3", extraIntent3); + + extraIntent = intent.getParcelableExtra("EXTRA_INTENT", Intent.class); + extraIntent2 = intent.getParcelableExtra("EXTRA_INTENT2", Intent.class); + extraIntent3 = intent.getParcelableExtra("EXTRA_INTENT3", Intent.class); + + assertThat(extraIntent.getExtendedFlags() + & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0); + // sneaked in intent should have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set. + assertThat(extraIntent2.getExtendedFlags() + & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isNotEqualTo(0); + // local created intent should not have EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN set. + assertThat(extraIntent3.getExtendedFlags() + & Intent.EXTENDED_FLAG_MISSING_CREATOR_OR_INVALID_TOKEN).isEqualTo(0); } private void verifyWaitingForNetworkStateUpdate(long curProcStateSeq, |