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

Commit 62ee3bce authored by Hai Zhang's avatar Hai Zhang
Browse files

Add signature permission allowlist.

Signature permission allowlist works in a very similar way to privileged
permission and OEM permission allowlists, but is only applicable to apps
that are not preinstalled, i.e. preinstalled apps automatically get all
their signature permissions allowlisted.

If a non-preinstalled platform-signed app tries to obtain a platform
signature permission, it can now only obtain the permission when its
package name and the permission is in this new signature permission
allowlist - otherwise the permission won't be granted as if the app
weren't platform-signed.

Debuggable builds are exempt from this allowlist requirement and the
platform signature permissions may still be granted despite a warning.

Bug: 308573169
Test: manual, automated test approach is still being discussed.
Change-Id: Iccee5b189ac807f9d62984d6e20c3ed5f91a9143
parent f7a526b9
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -78,3 +78,11 @@ flag {
    description: "This flag is used to enabled the Wallet Role for all users on the device"
    bug: "283989236"
}

flag {
  name: "signature_permission_allowlist_enabled"
  is_fixed_read_only: true
  namespace: "permissions"
  description: "Enable signature permission allowlist"
  bug: "308573169"
}
+45 −4
Original line number Diff line number Diff line
@@ -95,6 +95,7 @@ public class SystemConfig {
    private static final int ALLOW_OVERRIDE_APP_RESTRICTIONS = 0x100;
    private static final int ALLOW_IMPLICIT_BROADCASTS = 0x200;
    private static final int ALLOW_VENDOR_APEX = 0x400;
    private static final int ALLOW_SIGNATURE_PERMISSIONS = 0x800;
    private static final int ALLOW_ALL = ~0;

    // property for runtime configuration differentiation
@@ -597,7 +598,7 @@ public class SystemConfig {

        // Vendors are only allowed to customize these
        int vendorPermissionFlag = ALLOW_LIBS | ALLOW_FEATURES | ALLOW_PRIVAPP_PERMISSIONS
                | ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX;
                | ALLOW_SIGNATURE_PERMISSIONS | ALLOW_ASSOCIATIONS | ALLOW_VENDOR_APEX;
        if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.O_MR1) {
            // For backward compatibility
            vendorPermissionFlag |= (ALLOW_PERMISSIONS | ALLOW_APP_CONFIGS);
@@ -649,9 +650,9 @@ public class SystemConfig {
        // TODO(b/157203468): ALLOW_HIDDENAPI_WHITELISTING must be removed because we prohibited
        // the use of hidden APIs from the product partition.
        int productPermissionFlag = ALLOW_FEATURES | ALLOW_LIBS | ALLOW_PERMISSIONS
                | ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS | ALLOW_HIDDENAPI_WHITELISTING
                | ALLOW_ASSOCIATIONS | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS
                | ALLOW_VENDOR_APEX;
                | ALLOW_APP_CONFIGS | ALLOW_PRIVAPP_PERMISSIONS | ALLOW_SIGNATURE_PERMISSIONS
                | ALLOW_HIDDENAPI_WHITELISTING | ALLOW_ASSOCIATIONS
                | ALLOW_OVERRIDE_APP_RESTRICTIONS | ALLOW_IMPLICIT_BROADCASTS | ALLOW_VENDOR_APEX;
        if (Build.VERSION.DEVICE_INITIAL_SDK_INT <= Build.VERSION_CODES.R) {
            // TODO(b/157393157): This must check product interface enforcement instead of
            // DEVICE_INITIAL_SDK_INT for the devices without product interface enforcement.
@@ -772,6 +773,8 @@ public class SystemConfig {
            final boolean allowAppConfigs = (permissionFlag & ALLOW_APP_CONFIGS) != 0;
            final boolean allowPrivappPermissions = (permissionFlag & ALLOW_PRIVAPP_PERMISSIONS)
                    != 0;
            final boolean allowSignaturePermissions = (permissionFlag & ALLOW_SIGNATURE_PERMISSIONS)
                    != 0;
            final boolean allowOemPermissions = (permissionFlag & ALLOW_OEM_PERMISSIONS) != 0;
            final boolean allowApiWhitelisting = (permissionFlag & ALLOW_HIDDENAPI_WHITELISTING)
                    != 0;
@@ -1246,6 +1249,38 @@ public class SystemConfig {
                            XmlUtils.skipCurrentTag(parser);
                        }
                    } break;
                    case "signature-permissions": {
                        if (allowSignaturePermissions) {
                            // signature permissions from system, apex, vendor, product and
                            // system_ext partitions are stored separately. This is to
                            // prevent xml files in the vendor partition from granting
                            // permissions to signature apps in the system partition and vice versa.
                            boolean vendor = permFile.toPath().startsWith(
                                    Environment.getVendorDirectory().toPath() + "/")
                                    || permFile.toPath().startsWith(
                                    Environment.getOdmDirectory().toPath() + "/");
                            boolean product = permFile.toPath().startsWith(
                                    Environment.getProductDirectory().toPath() + "/");
                            boolean systemExt = permFile.toPath().startsWith(
                                    Environment.getSystemExtDirectory().toPath() + "/");
                            if (vendor) {
                                readSignatureAppPermissions(parser,
                                        mPermissionAllowlist.getVendorSignatureAppAllowlist());
                            } else if (product) {
                                readSignatureAppPermissions(parser,
                                        mPermissionAllowlist.getProductSignatureAppAllowlist());
                            } else if (systemExt) {
                                readSignatureAppPermissions(parser,
                                        mPermissionAllowlist.getSystemExtSignatureAppAllowlist());
                            } else {
                                readSignatureAppPermissions(parser,
                                        mPermissionAllowlist.getSignatureAppAllowlist());
                            }
                        } else {
                            logNotAllowedInPartition(name, permFile, parser);
                            XmlUtils.skipCurrentTag(parser);
                        }
                    } break;
                    case "oem-permissions": {
                        if (allowOemPermissions) {
                            readOemPermissions(parser);
@@ -1655,6 +1690,12 @@ public class SystemConfig {
        readPermissionAllowlist(parser, allowlist, "privapp-permissions");
    }

    private void readSignatureAppPermissions(@NonNull XmlPullParser parser,
            @NonNull ArrayMap<String, ArrayMap<String, Boolean>> allowlist)
            throws IOException, XmlPullParserException {
        readPermissionAllowlist(parser, allowlist, "signature-permissions");
    }

    private void readInstallInUserType(XmlPullParser parser,
            Map<String, Set<String>> doInstallMap,
            Map<String, Set<String>> nonInstallMap)
+74 −0
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import android.util.ArrayMap;
public final class PermissionAllowlist {
    @NonNull
    private final ArrayMap<String, ArrayMap<String, Boolean>> mOemAppAllowlist = new ArrayMap<>();

    @NonNull
    private final ArrayMap<String, ArrayMap<String, Boolean>> mPrivilegedAppAllowlist =
            new ArrayMap<>();
@@ -42,6 +43,19 @@ public final class PermissionAllowlist {
    private final ArrayMap<String, ArrayMap<String, ArrayMap<String, Boolean>>>
            mApexPrivilegedAppAllowlists = new ArrayMap<>();

    @NonNull
    private final ArrayMap<String, ArrayMap<String, Boolean>> mSignatureAppAllowlist =
            new ArrayMap<>();
    @NonNull
    private final ArrayMap<String, ArrayMap<String, Boolean>> mVendorSignatureAppAllowlist =
            new ArrayMap<>();
    @NonNull
    private final ArrayMap<String, ArrayMap<String, Boolean>> mProductSignatureAppAllowlist =
            new ArrayMap<>();
    @NonNull
    private final ArrayMap<String, ArrayMap<String, Boolean>> mSystemExtSignatureAppAllowlist =
            new ArrayMap<>();

    @NonNull
    public ArrayMap<String, ArrayMap<String, Boolean>> getOemAppAllowlist() {
        return mOemAppAllowlist;
@@ -73,6 +87,26 @@ public final class PermissionAllowlist {
        return mApexPrivilegedAppAllowlists;
    }

    @NonNull
    public ArrayMap<String, ArrayMap<String, Boolean>> getSignatureAppAllowlist() {
        return mSignatureAppAllowlist;
    }

    @NonNull
    public ArrayMap<String, ArrayMap<String, Boolean>> getVendorSignatureAppAllowlist() {
        return mVendorSignatureAppAllowlist;
    }

    @NonNull
    public ArrayMap<String, ArrayMap<String, Boolean>> getProductSignatureAppAllowlist() {
        return mProductSignatureAppAllowlist;
    }

    @NonNull
    public ArrayMap<String, ArrayMap<String, Boolean>> getSystemExtSignatureAppAllowlist() {
        return mSystemExtSignatureAppAllowlist;
    }

    @Nullable
    public Boolean getOemAppAllowlistState(@NonNull String packageName,
            @NonNull String permissionName) {
@@ -137,4 +171,44 @@ public final class PermissionAllowlist {
        }
        return permissions.get(permissionName);
    }

    @Nullable
    public Boolean getSignatureAppAllowlistState(@NonNull String packageName,
            @NonNull String permissionName) {
        ArrayMap<String, Boolean> permissions = mSignatureAppAllowlist.get(packageName);
        if (permissions == null) {
            return null;
        }
        return permissions.get(permissionName);
    }

    @Nullable
    public Boolean getVendorSignatureAppAllowlistState(@NonNull String packageName,
            @NonNull String permissionName) {
        ArrayMap<String, Boolean> permissions = mVendorSignatureAppAllowlist.get(packageName);
        if (permissions == null) {
            return null;
        }
        return permissions.get(permissionName);
    }

    @Nullable
    public Boolean getProductSignatureAppAllowlistState(@NonNull String packageName,
            @NonNull String permissionName) {
        ArrayMap<String, Boolean> permissions = mProductSignatureAppAllowlist.get(packageName);
        if (permissions == null) {
            return null;
        }
        return permissions.get(permissionName);
    }

    @Nullable
    public Boolean getSystemExtSignatureAppAllowlistState(@NonNull String packageName,
            @NonNull String permissionName) {
        ArrayMap<String, Boolean> permissions = mSystemExtSignatureAppAllowlist.get(packageName);
        if (permissions == null) {
            return null;
        }
        return permissions.get(permissionName);
    }
}
+73 −7
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.content.pm.PermissionGroupInfo
import android.content.pm.PermissionInfo
import android.content.pm.SigningDetails
import android.os.Build
import android.permission.flags.Flags
import android.util.Slog
import com.android.internal.os.RoSystemProperties
import com.android.internal.pm.permission.CompatibilityPermissionInfo
@@ -1193,7 +1194,8 @@ class AppIdPermissionPolicy : SchemePolicy() {
            newState.externalState.packageStates[PLATFORM_PACKAGE_NAME]!!
                .androidPackage!!
                .signingDetails
        return sourceSigningDetails?.hasCommonSignerWithCapability(
        val hasCommonSigner =
            sourceSigningDetails?.hasCommonSignerWithCapability(
                packageSigningDetails,
                SigningDetails.CertCapabilities.PERMISSION
            ) == true ||
@@ -1202,6 +1204,70 @@ class AppIdPermissionPolicy : SchemePolicy() {
                    packageSigningDetails,
                    SigningDetails.CertCapabilities.PERMISSION
                )
        if (!Flags.signaturePermissionAllowlistEnabled()) {
            return hasCommonSigner;
        }
        if (!hasCommonSigner) {
            return false
        }
        // A platform signature permission also needs to be allowlisted on non-debuggable builds.
        if (permission.packageName == PLATFORM_PACKAGE_NAME) {
            val isRequestedByFactoryApp =
                if (packageState.isSystem) {
                    // For updated system applications, a signature permission still needs to be
                    // allowlisted if it wasn't requested by the original application.
                    if (packageState.isUpdatedSystemApp) {
                        val disabledSystemPackage =
                            newState.externalState.disabledSystemPackageStates[
                                    packageState.packageName]
                                ?.androidPackage
                        disabledSystemPackage != null &&
                            permission.name in disabledSystemPackage.requestedPermissions
                    } else {
                        true
                    }
                } else {
                    false
                }
            if (
                !(isRequestedByFactoryApp ||
                    getSignaturePermissionAllowlistState(packageState, permission.name) == true)
            ) {
                Slog.w(
                    LOG_TAG,
                    "Signature permission ${permission.name} for package" +
                        " ${packageState.packageName} (${packageState.path}) not in" +
                        " signature permission allowlist"
                )
                if (!Build.isDebuggable()) {
                    return false
                }
            }
        }
        return true
    }

    private fun MutateStateScope.getSignaturePermissionAllowlistState(
        packageState: PackageState,
        permissionName: String
    ): Boolean? {
        val permissionAllowlist = newState.externalState.permissionAllowlist
        val packageName = packageState.packageName
        return when {
            packageState.isVendor || packageState.isOdm ->
                permissionAllowlist.getVendorSignatureAppAllowlistState(packageName, permissionName)
            packageState.isProduct ->
                permissionAllowlist.getProductSignatureAppAllowlistState(
                    packageName,
                    permissionName
                )
            packageState.isSystemExt ->
                permissionAllowlist.getSystemExtSignatureAppAllowlistState(
                    packageName,
                    permissionName
                )
            else -> permissionAllowlist.getSignatureAppAllowlistState(packageName, permissionName)
        }
    }

    private fun MutateStateScope.checkPrivilegedPermissionAllowlist(