From d21447ccd299f1b1a1b54590063272f1fdd48501 Mon Sep 17 00:00:00 2001 From: Andrii Kulian Date: Wed, 9 Feb 2022 13:56:11 -0800 Subject: [PATCH] Add Cross-UID ActivityEmbedding trust model APIs By default activity embedding should be restricted, and apps have to opt in explicitly to allow other hosts to embed them. An app can declare a list of known trusted embedding host certificates in AndroidManifest for individual activities or entire application. Alternatively, an app can allow embedding by all hosts in untrusted mode. In this case interactions and operations that the client host can perform on the embedded container will be limited to prevent tapjacking and overlay attacks. Bug: 197364677 Test: ActivityEmbeddingCrossUidTests Change-Id: I78c303237fdc77bdf55bd74580ac0b19c8de622b --- core/api/current.txt | 5 ++ .../java/android/content/pm/ActivityInfo.java | 62 ++++++++++++++++++- .../android/content/pm/ApplicationInfo.java | 52 +++++++++++++++- .../android/content/package_item_info.proto | 1 + core/res/res/values/attrs_manifest.xml | 23 +++++++ core/res/res/values/public.xml | 2 + .../pm/pkg/component/ParsedActivity.java | 8 +++ .../pm/pkg/component/ParsedActivityImpl.java | 40 ++++++++++-- .../pm/pkg/component/ParsedActivityUtils.java | 18 +++++- .../parsing/PackageInfoWithoutStateUtils.java | 1 + .../server/pm/pkg/parsing/ParsingPackage.java | 6 ++ .../pm/pkg/parsing/ParsingPackageImpl.java | 22 +++++++ .../pm/pkg/parsing/ParsingPackageUtils.java | 15 +++++ .../server/pm/pkg/parsing/ParsingUtils.java | 49 +++++++++++++++ .../pkg/parsing/PkgWithoutStateAppInfo.java | 9 +++ .../parsing/parcelling/AndroidPackageTest.kt | 3 +- .../parsing/parcelling/ParsedActivityTest.kt | 5 +- .../android/server/pm/PackageParserTest.java | 1 + 18 files changed, 312 insertions(+), 10 deletions(-) diff --git a/core/api/current.txt b/core/api/current.txt index 928c011e3d6ce..ca7388427859f 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -341,6 +341,7 @@ package android { field public static final int allowSingleTap = 16843353; // 0x1010259 field public static final int allowTaskReparenting = 16843268; // 0x1010204 field public static final int allowUndo = 16843999; // 0x10104df + field public static final int allowUntrustedActivityEmbedding; field public static final int alpha = 16843551; // 0x101031f field public static final int alphabeticModifiers = 16844110; // 0x101054e field public static final int alphabeticShortcut = 16843235; // 0x10101e3 @@ -905,6 +906,7 @@ package android { field public static final int keyboardNavigationCluster = 16844096; // 0x1010540 field public static final int keycode = 16842949; // 0x10100c5 field public static final int killAfterRestore = 16843420; // 0x101029c + field public static final int knownActivityEmbeddingCerts; field public static final int knownCerts = 16844330; // 0x101062a field public static final int lStar = 16844359; // 0x1010647 field public static final int label = 16842753; // 0x1010001 @@ -10966,6 +10968,7 @@ package android.content.pm { ctor public ActivityInfo(android.content.pm.ActivityInfo); method public int describeContents(); method public void dump(android.util.Printer, String); + method @NonNull public java.util.Set getKnownActivityEmbeddingCerts(); method public final int getThemeResource(); field public static final int COLOR_MODE_DEFAULT = 0; // 0x0 field public static final int COLOR_MODE_HDR = 2; // 0x2 @@ -10993,6 +10996,7 @@ package android.content.pm { field public static final int DOCUMENT_LAUNCH_NEVER = 3; // 0x3 field public static final int DOCUMENT_LAUNCH_NONE = 0; // 0x0 field public static final int FLAG_ALLOW_TASK_REPARENTING = 64; // 0x40 + field public static final int FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING = 268435456; // 0x10000000 field public static final int FLAG_ALWAYS_RETAIN_TASK_STATE = 8; // 0x8 field public static final int FLAG_AUTO_REMOVE_FROM_RECENTS = 8192; // 0x2000 field public static final int FLAG_CLEAR_TASK_ON_LAUNCH = 4; // 0x4 @@ -11082,6 +11086,7 @@ package android.content.pm { method public void dump(android.util.Printer, String); method public static CharSequence getCategoryTitle(android.content.Context, int); method public int getGwpAsanMode(); + method @NonNull public java.util.Set getKnownActivityEmbeddingCerts(); method public int getMemtagMode(); method public int getNativeHeapZeroInitialized(); method public int getRequestRawExternalStorageAccess(); diff --git a/core/java/android/content/pm/ActivityInfo.java b/core/java/android/content/pm/ActivityInfo.java index 8bea006071bf6..0673b3ad5b0a6 100644 --- a/core/java/android/content/pm/ActivityInfo.java +++ b/core/java/android/content/pm/ActivityInfo.java @@ -18,6 +18,8 @@ package android.content.pm; import android.annotation.FloatRange; import android.annotation.IntDef; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.annotation.TestApi; import android.app.Activity; import android.app.compat.CompatChanges; @@ -35,10 +37,16 @@ import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.os.UserHandle; +import android.util.ArraySet; import android.util.Printer; +import com.android.internal.util.Parcelling; + import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.Collections; +import java.util.Locale; +import java.util.Set; /** * Information you can retrieve about a particular application @@ -48,6 +56,9 @@ import java.lang.annotation.RetentionPolicy; */ public class ActivityInfo extends ComponentInfo implements Parcelable { + private static final Parcelling.BuiltIn.ForStringSet sForStringSet = + Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class); + // NOTE: When adding new data members be sure to update the copy-constructor, Parcel // constructor, and writeToParcel. @@ -524,6 +535,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { */ public static final int FLAG_PREFER_MINIMAL_POST_PROCESSING = 0x2000000; + /** + * Bit in {@link #flags}: If set, indicates that the activity can be embedded by untrusted + * hosts. In this case the interactions with and visibility of the embedded activity may be + * limited. Set from the {@link android.R.attr#allowUntrustedActivityEmbedding} attribute. + */ + public static final int FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING = 0x10000000; + /** * @hide Bit in {@link #flags}: If set, this component will only be seen * by the system user. Only works with broadcast receivers. Set from the @@ -561,7 +579,8 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { * {@link #FLAG_STATE_NOT_NEEDED}, {@link #FLAG_EXCLUDE_FROM_RECENTS}, * {@link #FLAG_ALLOW_TASK_REPARENTING}, {@link #FLAG_NO_HISTORY}, * {@link #FLAG_FINISH_ON_CLOSE_SYSTEM_DIALOGS}, - * {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER}. + * {@link #FLAG_HARDWARE_ACCELERATED}, {@link #FLAG_SINGLE_USER}, + * {@link #FLAG_ALLOW_UNTRUSTED_ACTIVITY_EMBEDDING}. */ public int flags; @@ -1079,6 +1098,13 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.S) private static final long CHECK_MIN_WIDTH_HEIGHT_FOR_MULTI_WINDOW = 197654537L; + /** + * Optional set of a certificates identifying apps that are allowed to embed this activity. From + * the "knownActivityEmbeddingCerts" attribute. + */ + @Nullable + private Set mKnownActivityEmbeddingCerts; + /** * Convert Java change bits to native. * @@ -1227,6 +1253,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { launchMode = orig.launchMode; documentLaunchMode = orig.documentLaunchMode; permission = orig.permission; + mKnownActivityEmbeddingCerts = orig.mKnownActivityEmbeddingCerts; taskAffinity = orig.taskAffinity; targetActivity = orig.targetActivity; flags = orig.flags; @@ -1442,6 +1469,31 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { return mMinAspectRatio; } + /** + * Gets the trusted host certificate digests of apps that are allowed to embed this activity. + * The digests are computed using the SHA-256 digest algorithm. + * @see android.R.attr#knownActivityEmbeddingCerts + */ + @NonNull + public Set getKnownActivityEmbeddingCerts() { + return mKnownActivityEmbeddingCerts == null ? Collections.emptySet() + : mKnownActivityEmbeddingCerts; + } + + /** + * Sets the trusted host certificates of apps that are allowed to embed this activity. + * @see #getKnownActivityEmbeddingCerts() + * @hide + */ + public void setKnownActivityEmbeddingCerts(@NonNull Set knownActivityEmbeddingCerts) { + // Convert the provided digest to upper case for consistent Set membership + // checks when verifying the signing certificate digests of requesting apps. + mKnownActivityEmbeddingCerts = new ArraySet<>(); + for (String knownCert : knownActivityEmbeddingCerts) { + mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US)); + } + } + private boolean isChangeEnabled(long changeId) { return CompatChanges.isChangeEnabled(changeId, applicationInfo.packageName, UserHandle.getUserHandleForUid(applicationInfo.uid)); @@ -1573,6 +1625,9 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { if (supportsSizeChanges) { pw.println(prefix + "supportsSizeChanges=true"); } + if (mKnownActivityEmbeddingCerts != null) { + pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts); + } super.dumpBack(pw, prefix, dumpFlags); } @@ -1618,6 +1673,7 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { dest.writeFloat(mMaxAspectRatio); dest.writeFloat(mMinAspectRatio); dest.writeBoolean(supportsSizeChanges); + sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags); } /** @@ -1739,6 +1795,10 @@ public class ActivityInfo extends ComponentInfo implements Parcelable { mMaxAspectRatio = source.readFloat(); mMinAspectRatio = source.readFloat(); supportsSizeChanges = source.readBoolean(); + mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source); + if (mKnownActivityEmbeddingCerts.isEmpty()) { + mKnownActivityEmbeddingCerts = null; + } } /** diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index 567f649ea7623..2528e16ce7b70 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -35,6 +35,7 @@ import android.os.Parcelable; import android.os.UserHandle; import android.os.storage.StorageManager; import android.util.ArrayMap; +import android.util.ArraySet; import android.util.Printer; import android.util.SparseArray; import android.util.proto.ProtoOutputStream; @@ -52,7 +53,9 @@ import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.List; +import java.util.Locale; import java.util.Objects; +import java.util.Set; import java.util.UUID; /** @@ -62,6 +65,8 @@ import java.util.UUID; */ public class ApplicationInfo extends PackageItemInfo implements Parcelable { private static ForBoolean sForBoolean = Parcelling.Cache.getOrCreate(ForBoolean.class); + private static final Parcelling.BuiltIn.ForStringSet sForStringSet = + Parcelling.Cache.getOrCreate(Parcelling.BuiltIn.ForStringSet.class); /** * Default task affinity of all activities in this application. See @@ -1550,6 +1555,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { */ private int localeConfigRes; + /** + * Optional set of a certificates identifying apps that are allowed to embed activities of this + * application. From the "knownActivityEmbeddingCerts" attribute. + */ + @Nullable + private Set mKnownActivityEmbeddingCerts; + public void dump(Printer pw, String prefix) { dump(pw, prefix, DUMP_FLAG_ALL); } @@ -1673,6 +1685,9 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } } pw.println(prefix + "createTimestamp=" + createTimestamp); + if (mKnownActivityEmbeddingCerts != null) { + pw.println(prefix + "knownActivityEmbeddingCerts=" + mKnownActivityEmbeddingCerts); + } super.dumpBack(pw, prefix); } @@ -1787,6 +1802,11 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } proto.end(detailToken); } + if (!ArrayUtils.isEmpty(mKnownActivityEmbeddingCerts)) { + for (String knownCert : mKnownActivityEmbeddingCerts) { + proto.write(ApplicationInfoProto.KNOWN_ACTIVITY_EMBEDDING_CERTS, knownCert); + } + } proto.end(token); } @@ -1837,6 +1857,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { super(orig); taskAffinity = orig.taskAffinity; permission = orig.permission; + mKnownActivityEmbeddingCerts = orig.mKnownActivityEmbeddingCerts; processName = orig.processName; className = orig.className; theme = orig.theme; @@ -2006,6 +2027,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } } dest.writeInt(localeConfigRes); + sForStringSet.parcel(mKnownActivityEmbeddingCerts, dest, flags); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR @@ -2102,6 +2124,10 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } } localeConfigRes = source.readInt(); + mKnownActivityEmbeddingCerts = sForStringSet.unparcel(source); + if (mKnownActivityEmbeddingCerts.isEmpty()) { + mKnownActivityEmbeddingCerts = null; + } } /** @@ -2658,7 +2684,6 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return localeConfigRes; } - /** * List of all shared libraries this application is linked against. This * list will only be set if the {@link PackageManager#GET_SHARED_LIBRARY_FILES @@ -2675,4 +2700,29 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { return sharedLibraryInfos; } + /** + * Gets the trusted host certificate digests of apps that are allowed to embed activities of + * this application. The digests are computed using the SHA-256 digest algorithm. + * @see android.R.attr#knownActivityEmbeddingCerts + */ + @NonNull + public Set getKnownActivityEmbeddingCerts() { + return mKnownActivityEmbeddingCerts == null ? Collections.emptySet() + : mKnownActivityEmbeddingCerts; + } + + /** + * Sets the trusted host certificates of apps that are allowed to embed activities of this + * application. + * @see #getKnownActivityEmbeddingCerts() + * @hide + */ + public void setKnownActivityEmbeddingCerts(@NonNull Set knownActivityEmbeddingCerts) { + // Convert the provided digest to upper case for consistent Set membership + // checks when verifying the signing certificate digests of requesting apps. + mKnownActivityEmbeddingCerts = new ArraySet<>(); + for (String knownCert : knownActivityEmbeddingCerts) { + mKnownActivityEmbeddingCerts.add(knownCert.toUpperCase(Locale.US)); + } + } } diff --git a/core/proto/android/content/package_item_info.proto b/core/proto/android/content/package_item_info.proto index 5c6116a09b770..279a5d0c17f84 100644 --- a/core/proto/android/content/package_item_info.proto +++ b/core/proto/android/content/package_item_info.proto @@ -115,4 +115,5 @@ message ApplicationInfoProto { } optional Detail detail = 17; repeated string overlay_paths = 18; + repeated string known_activity_embedding_certs = 19; } diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index cb40e86f15359..6dc975b4112c5 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -1048,6 +1048,24 @@

The default value of this attribute is false. --> + + + + + + + + +