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

Commit 199d5d93 authored by Brad Ebinger's avatar Brad Ebinger
Browse files

Ensure installed packages do not leak when calling placeCall

By checking if the target PhoneAccountHandle packageName matches
the calling package before determining selfManaged attributes,
we stop the leak of installed packages that may be associated
with a specific test PhoneAccountHandle.

Bug: 231988638
Bug: 265321471
Test: atest TelecomUnitTests; atest CtsTelecomTestCases
Change-Id: I1ab81c69a32f398152fb5e00615bd395f96dadba
parent e0a031df
Loading
Loading
Loading
Loading
+35 −31
Original line number Original line Diff line number Diff line
@@ -1743,7 +1743,6 @@ public class TelecomServiceImpl {
                enforceCallingPackage(callingPackage, "placeCall");
                enforceCallingPackage(callingPackage, "placeCall");


                PhoneAccountHandle phoneAccountHandle = null;
                PhoneAccountHandle phoneAccountHandle = null;
                boolean clearPhoneAccountHandleExtra = false;
                if (extras != null) {
                if (extras != null) {
                    phoneAccountHandle = extras.getParcelable(
                    phoneAccountHandle = extras.getParcelable(
                            TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
                            TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
@@ -1752,33 +1751,42 @@ public class TelecomServiceImpl {
                        extras.remove(TelecomManager.EXTRA_IS_HANDOVER);
                        extras.remove(TelecomManager.EXTRA_IS_HANDOVER);
                    }
                    }
                }
                }
                boolean isSelfManaged = phoneAccountHandle != null &&
                ComponentName phoneAccountComponentName = phoneAccountHandle != null
                        ? phoneAccountHandle.getComponentName() : null;
                String phoneAccountPackageName = phoneAccountComponentName != null
                        ? phoneAccountComponentName.getPackageName() : null;
                boolean isCallerOwnerOfPhoneAccount =
                        callingPackage.equals(phoneAccountPackageName);
                boolean isSelfManagedPhoneAccount =
                        isSelfManagedConnectionService(phoneAccountHandle);
                        isSelfManagedConnectionService(phoneAccountHandle);
                if (isSelfManaged) {
                // Ensure the app's calling package matches the PhoneAccount package name before
                    try {
                // checking self-managed status so that we do not leak installed package
                // information.
                boolean isSelfManagedRequest = isCallerOwnerOfPhoneAccount &&
                        isSelfManagedPhoneAccount;
                if (isSelfManagedRequest) {
                    // The package name of the caller matches the package name of the
                    // PhoneAccountHandle, so ensure the app has MANAGE_OWN_CALLS permission if
                    // self-managed.
                    mContext.enforceCallingOrSelfPermission(
                    mContext.enforceCallingOrSelfPermission(
                            Manifest.permission.MANAGE_OWN_CALLS,
                            Manifest.permission.MANAGE_OWN_CALLS,
                                "Self-managed ConnectionServices require "
                            "Self-managed ConnectionServices require MANAGE_OWN_CALLS permission.");
                                        + "MANAGE_OWN_CALLS permission.");
                } else if (!canCallPhone(callingPackage, callingFeatureId,
                    } catch (SecurityException e) {
                        // Fallback to use mobile network to avoid disclosing phone account handle
                        // package information
                        clearPhoneAccountHandleExtra = true;
                    }

                    if (!clearPhoneAccountHandleExtra && !callingPackage.equals(
                            phoneAccountHandle.getComponentName().getPackageName())
                            && !canCallPhone(callingPackage, callingFeatureId,
                        "CALL_PHONE permission required to place calls.")) {
                        "CALL_PHONE permission required to place calls.")) {
                        // The caller is not allowed to place calls, so fallback to use mobile
                    // not self-managed, so CALL_PHONE is required.
                        // network.
                        clearPhoneAccountHandleExtra = true;
                    }
                } else if (!canCallPhone(callingPackage, callingFeatureId, "placeCall")) {
                    mAnomalyReporter.reportAnomaly(PLACE_CALL_SECURITY_EXCEPTION_ERROR_UUID,
                    mAnomalyReporter.reportAnomaly(PLACE_CALL_SECURITY_EXCEPTION_ERROR_UUID,
                            PLACE_CALL_SECURITY_EXCEPTION_ERROR_MSG);
                            PLACE_CALL_SECURITY_EXCEPTION_ERROR_MSG);
                    throw new SecurityException("Package " + callingPackage
                    throw new SecurityException(
                            + " is not allowed to place phone calls");
                            "CALL_PHONE permission required to place calls.");
                }

                // An application can not place a call with a self-managed PhoneAccount that
                // they do not own. If this is the case (and the app has CALL_PHONE permission),
                // remove the PhoneAccount from the request and place the call as if it was a
                // managed call request with no PhoneAccount specified.
                if (!isCallerOwnerOfPhoneAccount && isSelfManagedPhoneAccount) {
                    // extras can not be null if isSelfManagedPhoneAccount is true
                    extras.remove(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
                }
                }


                // Note: we can still get here for the default/system dialer, even if the Phone
                // Note: we can still get here for the default/system dialer, even if the Phone
@@ -1809,15 +1817,11 @@ public class TelecomServiceImpl {
                        final Intent intent = new Intent(hasCallPrivilegedPermission ?
                        final Intent intent = new Intent(hasCallPrivilegedPermission ?
                                Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
                                Intent.ACTION_CALL_PRIVILEGED : Intent.ACTION_CALL, handle);
                        if (extras != null) {
                        if (extras != null) {
                            if (clearPhoneAccountHandleExtra) {
                                extras.remove(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE);
                            }
                            extras.setDefusable(true);
                            extras.setDefusable(true);
                            intent.putExtras(extras);
                            intent.putExtras(extras);
                        }
                        }
                        mUserCallIntentProcessorFactory.create(mContext, userHandle)
                        mUserCallIntentProcessorFactory.create(mContext, userHandle)
                                .processIntent(
                                .processIntent(intent, callingPackage, isSelfManagedRequest,
                                        intent, callingPackage, isSelfManaged,
                                        (hasCallAppOp && hasCallPermission),
                                        (hasCallAppOp && hasCallPermission),
                                        true /* isLocalInvocation */);
                                        true /* isLocalInvocation */);
                    } finally {
                    } finally {
+9 −19
Original line number Original line Diff line number Diff line
@@ -1214,20 +1214,14 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        // ConnectionService.
        // ConnectionService.
        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);
        extras.putParcelable(TelecomManager.EXTRA_PHONE_ACCOUNT_HANDLE, TEL_PA_HANDLE_CURRENT);


        doReturn(PackageManager.PERMISSION_DENIED)
                .when(mContext).checkCallingPermission(CALL_PHONE);
        doReturn(PackageManager.PERMISSION_DENIED)
                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
        doThrow(new SecurityException()).when(mContext).enforceCallingOrSelfPermission(
                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());


        try {
        try {
            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME, null);
            placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
            fail("Expected SecurityException because MANAGE_OWN_CALLS is not set");
                    /*shouldNonEmergencyBeAllowed*/ false);
        } catch(SecurityException e) {
        } catch(SecurityException e) {
            fail("Unexpected SecurityException - MANAGE_OWN_CALLS is not set, so call should be "
            // expected
                    + "placed with no PhoneAccount");
        }
        }
    }
    }


@@ -1283,21 +1277,15 @@ public class TelecomServiceImplTest extends TelecomTestCase {
        // pass MANAGE_OWN_CALLS check, but do not have CALL PHONE
        // pass MANAGE_OWN_CALLS check, but do not have CALL PHONE
        doNothing().when(mContext).enforceCallingOrSelfPermission(
        doNothing().when(mContext).enforceCallingOrSelfPermission(
                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
                eq(Manifest.permission.MANAGE_OWN_CALLS), anyString());
        doReturn(PackageManager.PERMISSION_GRANTED)
                .when(mContext).checkCallingPermission(CALL_PHONE);
        doReturn(PackageManager.PERMISSION_DENIED)
                .when(mContext).checkCallingPermission(CALL_PRIVILEGED);
        doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
        doNothing().when(mContext).enforceCallingOrSelfPermission(eq(CALL_PHONE), anyString());
        when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
        when(mAppOpsManager.noteOp(eq(AppOpsManager.OP_CALL_PHONE), anyInt(), anyString(),
                nullable(String.class), nullable(String.class)))
                nullable(String.class), nullable(String.class)))
                .thenReturn(AppOpsManager.MODE_ERRORED);
                .thenReturn(AppOpsManager.MODE_ERRORED);
        try {
        try {
            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
            mTSIBinder.placeCall(handle, extras, PACKAGE_NAME + "2", null);
            placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
            fail("Expected a SecurityException - CALL_PHONE app op is denied");
                    /*shouldNonEmergencyBeAllowed*/ false);
        } catch(SecurityException e) {
        } catch(SecurityException e) {
            fail("Unexpected SecurityException - MANAGE_OWN_CALLS is set, so call should be "
            // expected
                    + "placed with no PhoneAccount");
        }
        }
    }
    }


@@ -1338,7 +1326,7 @@ public class TelecomServiceImplTest extends TelecomTestCase {
            fail("Unexpected SecurityException - CTS is default dialer and MANAGE_OWN_CALLS is not"
            fail("Unexpected SecurityException - CTS is default dialer and MANAGE_OWN_CALLS is not"
                    + " required. Exception: " + e);
                    + " required. Exception: " + e);
        }
        }
        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ true,
        placeCallTestHelper(handle, extras, /*isSelfManagedExpected*/ false,
                /*shouldNonEmergencyBeAllowed*/ true);
                /*shouldNonEmergencyBeAllowed*/ true);
    }
    }


@@ -1408,8 +1396,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
    }
    }


    /**
    /**
     * Since this is a self-managed call being requested, even though we do not have CALL_PHONE
     * Since this is a self-managed call being requested, so ensure we report the call as
     * permissions, we still consider non-emergency allowed because this is a self-managed call.
     * self-managed and without non-emergency permissions.
     */
     */
    @SmallTest
    @SmallTest
    @Test
    @Test
@@ -2094,6 +2082,8 @@ public class TelecomServiceImplTest extends TelecomTestCase {
    }
    }


    private static boolean areBundlesEqual(Bundle b1, Bundle b2) {
    private static boolean areBundlesEqual(Bundle b1, Bundle b2) {
        if (b1.keySet().size() != b2.keySet().size()) return false;

        for (String key1 : b1.keySet()) {
        for (String key1 : b1.keySet()) {
            if (!b1.get(key1).equals(b2.get(key1))) {
            if (!b1.get(key1).equals(b2.get(key1))) {
                return false;
                return false;