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

Commit 6b0e9b46 authored by Grace Jia's avatar Grace Jia
Browse files

DO NOT MERGE

Fix security vulnerability issue for multi user call redirections.

Currently we won't check if the PhoneAccountHandle provided by a
CallRedirectionService has multi-user capability or belong to the same
user as the current user. Add the check and disconnect the call if this
is an unexpected cross-user call redirection.

Bug: 235098883
Test: CallsManagerTest, manual test with test app provided in
b/235098883.

Change-Id: Ia8b9468aa2bb8e3157c227e2617ff6a52e0af119
Merged-In: Ia8b9468aa2bb8e3157c227e2617ff6a52e0af119
(cherry picked from commit f29ab7e1)
parent e4519232
Loading
Loading
Loading
Loading
+15 −4
Original line number Original line Diff line number Diff line
@@ -2071,6 +2071,16 @@ public class CallsManager extends Call.ListenerBase
        boolean endEarly = false;
        boolean endEarly = false;
        String disconnectReason = "";
        String disconnectReason = "";
        String callRedirectionApp = mRoleManagerAdapter.getDefaultCallRedirectionApp();
        String callRedirectionApp = mRoleManagerAdapter.getDefaultCallRedirectionApp();
        PhoneAccount phoneAccount = mPhoneAccountRegistrar
                .getPhoneAccountUnchecked(phoneAccountHandle);
        if (phoneAccount != null
                && !phoneAccount.hasCapabilities(PhoneAccount.CAPABILITY_MULTI_USER)) {
            // Check if the phoneAccountHandle belongs to the current user
            if (phoneAccountHandle != null &&
                    !phoneAccountHandle.getUserHandle().equals(mCurrentUserHandle)) {
                phoneAccountHandle = null;
            }
        }


        boolean isPotentialEmergencyNumber;
        boolean isPotentialEmergencyNumber;
        try {
        try {
@@ -2105,9 +2115,9 @@ public class CallsManager extends Call.ListenerBase
            endEarly = true;
            endEarly = true;
            disconnectReason = "Null handle from Call Redirection Service";
            disconnectReason = "Null handle from Call Redirection Service";
        } else if (phoneAccountHandle == null) {
        } else if (phoneAccountHandle == null) {
            Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is null");
            Log.w(this, "onCallRedirectionComplete: phoneAccountHandle is unavailable");
            endEarly = true;
            endEarly = true;
            disconnectReason = "Null phoneAccountHandle from Call Redirection Service";
            disconnectReason = "Unavailable phoneAccountHandle from Call Redirection Service";
        } else if (isPotentialEmergencyNumber) {
        } else if (isPotentialEmergencyNumber) {
            Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call"
            Log.w(this, "onCallRedirectionComplete: emergency number %s is redirected from Call"
                    + " Redirection Service", handle.getSchemeSpecificPart());
                    + " Redirection Service", handle.getSchemeSpecificPart());
@@ -2128,6 +2138,7 @@ public class CallsManager extends Call.ListenerBase
            return;
            return;
        }
        }


        final PhoneAccountHandle finalPhoneAccountHandle = phoneAccountHandle;
        if (uiAction.equals(CallRedirectionProcessor.UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM)) {
        if (uiAction.equals(CallRedirectionProcessor.UI_TYPE_USER_DEFINED_ASK_FOR_CONFIRM)) {
            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMATION);
            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMATION);
            mPendingRedirectedOutgoingCall = call;
            mPendingRedirectedOutgoingCall = call;
@@ -2137,7 +2148,7 @@ public class CallsManager extends Call.ListenerBase
                        @Override
                        @Override
                        public void loggedRun() {
                        public void loggedRun() {
                            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMED);
                            Log.addEvent(call, LogUtils.Events.REDIRECTION_USER_CONFIRMED);
                            call.setTargetPhoneAccount(phoneAccountHandle);
                            call.setTargetPhoneAccount(finalPhoneAccountHandle);
                            placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn,
                            placeOutgoingCall(call, handle, gatewayInfo, speakerphoneOn,
                                    videoState);
                                    videoState);
                        }
                        }
@@ -2147,7 +2158,7 @@ public class CallsManager extends Call.ListenerBase
                    new Runnable("CM.oCRC", mLock) {
                    new Runnable("CM.oCRC", mLock) {
                        @Override
                        @Override
                        public void loggedRun() {
                        public void loggedRun() {
                            call.setTargetPhoneAccount(phoneAccountHandle);
                            call.setTargetPhoneAccount(finalPhoneAccountHandle);
                            placeOutgoingCall(call, handle, null, speakerphoneOn,
                            placeOutgoingCall(call, handle, null, speakerphoneOn,
                                    videoState);
                                    videoState);
                        }
                        }
+24 −0
Original line number Original line Diff line number Diff line
@@ -58,6 +58,9 @@ import android.telecom.CallerInfo;
import android.telecom.Connection;
import android.telecom.Connection;
import android.telecom.DisconnectCause;
import android.telecom.DisconnectCause;
import android.telecom.Log;
import android.telecom.Log;
import android.telecom.Log;
import android.telecom.DisconnectCause;
import android.telecom.GatewayInfo;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccount;
import android.telecom.PhoneAccountHandle;
import android.telecom.PhoneAccountHandle;
import android.telecom.TelecomManager;
import android.telecom.TelecomManager;
@@ -133,8 +136,12 @@ import java.util.concurrent.TimeUnit;
@RunWith(JUnit4.class)
@RunWith(JUnit4.class)
public class CallsManagerTest extends TelecomTestCase {
public class CallsManagerTest extends TelecomTestCase {
    private static final int TEST_TIMEOUT = 5000;  // milliseconds
    private static final int TEST_TIMEOUT = 5000;  // milliseconds
    private static final int SECONDARY_USER_ID = 12;
    private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
    private static final PhoneAccountHandle SIM_1_HANDLE = new PhoneAccountHandle(
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1");
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1");
    private static final PhoneAccountHandle SIM_1_HANDLE_SECONDARY = new PhoneAccountHandle(
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim1",
            new UserHandle(SECONDARY_USER_ID));
    private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle(
    private static final PhoneAccountHandle SIM_2_HANDLE = new PhoneAccountHandle(
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2");
            ComponentName.unflattenFromString("com.foo/.Blah"), "Sim2");
    private static final PhoneAccountHandle CONNECTION_MGR_1_HANDLE = new PhoneAccountHandle(
    private static final PhoneAccountHandle CONNECTION_MGR_1_HANDLE = new PhoneAccountHandle(
@@ -1625,6 +1632,23 @@ public class CallsManagerTest extends TelecomTestCase {
        verify(callSpy, never()).setDisconnectCause(any(DisconnectCause.class));
        verify(callSpy, never()).setDisconnectCause(any(DisconnectCause.class));
    }
    }


    @SmallTest
    @Test
    public void testCrossUserCallRedirectionEndEarlyForIncapablePhoneAccount() {
        when(mPhoneAccountRegistrar.getPhoneAccountUnchecked(eq(SIM_1_HANDLE_SECONDARY)))
                .thenReturn(SIM_1_ACCOUNT);
        mCallsManager.onUserSwitch(UserHandle.SYSTEM);

        Call callSpy = addSpyCall(CallState.NEW);
        mCallsManager.onCallRedirectionComplete(callSpy, TEST_ADDRESS, SIM_1_HANDLE_SECONDARY,
                new GatewayInfo("foo", TEST_ADDRESS2, TEST_ADDRESS), true /* speakerphoneOn */,
                VideoProfile.STATE_AUDIO_ONLY, false /* shouldCancelCall */, "" /* uiAction */);

        ArgumentCaptor<String> argumentCaptor = ArgumentCaptor.forClass(String.class);
        verify(callSpy).disconnect(argumentCaptor.capture());
        assertTrue(argumentCaptor.getValue().contains("Unavailable phoneAccountHandle"));
    }

    private Call addSpyCall() {
    private Call addSpyCall() {
        return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
        return addSpyCall(SIM_2_HANDLE, CallState.ACTIVE);
    }
    }