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

Commit 2ce733e0 authored by Mingming Cai's avatar Mingming Cai Committed by Gerrit Code Review
Browse files

Merge "Fix IMS Authentication leakage"

parents 395b4182 b16c9343
Loading
Loading
Loading
Loading
+84 −59
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.os.Handler;
import android.os.Message;
import android.os.Registrant;
import android.os.RegistrantList;
import android.os.SystemClock;
import android.telephony.SubscriptionInfo;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
@@ -138,8 +139,6 @@ public abstract class IccRecords extends Handler implements IccConstants {
    protected boolean mIsVoiceMailFixed = false;
    @UnsupportedAppUsage
    protected String mImsi; // IMSI must be only valid numeric characters 0-9 without padding 'f's
    @UnsupportedAppUsage
    private IccIoResult auth_rsp;

    @UnsupportedAppUsage
    protected int mMncLength = UNINITIALIZED;
@@ -174,9 +173,6 @@ public abstract class IccRecords extends Handler implements IccConstants {
    protected String[] mEhplmns;
    protected String[] mFplmns;

    @UnsupportedAppUsage
    private final Object mLock = new Object();

    CarrierTestOverride mCarrierTestOverride;

    //Arbitrary offset for the Handler
@@ -231,6 +227,20 @@ public abstract class IccRecords extends Handler implements IccConstants {
    public static final int DEFAULT_VOICE_MESSAGE_COUNT = -2;
    public static final int UNKNOWN_VOICE_MESSAGE_COUNT = -1;

    // Maximum time in millisecond to wait for a IccSim Challenge before assuming it will not
    // arrive and returning null to the callers.
    private static final long ICC_SIM_CHALLENGE_TIMEOUT_MILLIS = 2500;

    /**
     * There are two purposes for this class. First, each instance of AuthAsyncResponse acts as a
     * lock to for calling thead to wait in getIccSimChallengeResponse(). Second, pass the IMS
     * authentication response to the getIccSimChallengeResponse().
     */
    private static class AuthAsyncResponse {
        public IccIoResult authRsp;
        public Throwable exception;
    }

    @Override
    public String toString() {
        String iccIdToPrint = SubscriptionInfo.givePrintableIccid(mFullIccId);
@@ -311,13 +321,6 @@ public abstract class IccRecords extends Handler implements IccConstants {
    public void dispose() {
        mDestroyed.set(true);

        // It is possible that there is another thread waiting for the response
        // to requestIccSimAuthentication() in getIccSimChallengeResponse().
        auth_rsp = null;
        synchronized (mLock) {
            mLock.notifyAll();
        }

        mCi.unregisterForIccRefresh(this);
        mParentApp.unregisterForReady(this);
        mParentApp.unregisterForLocked(this);
@@ -890,20 +893,27 @@ public abstract class IccRecords extends Handler implements IccConstants {

            case EVENT_AKA_AUTHENTICATE_DONE:
                ar = (AsyncResult) msg.obj;
                auth_rsp = null;
                AuthAsyncResponse rsp = (AuthAsyncResponse) ar.userObj;
                if (DBG) log("EVENT_AKA_AUTHENTICATE_DONE");

                synchronized (rsp) {
                    if (ar.exception != null) {
                        rsp.exception = ar.exception;
                        loge("Exception ICC SIM AKA: " + ar.exception);
                    } else if (ar.result == null) {
                        rsp.exception = new NullPointerException(
                                "Null SIM authentication response");
                        loge("EVENT_AKA_AUTHENTICATE_DONE: null response");
                    } else {
                        try {
                        auth_rsp = (IccIoResult)ar.result;
                        if (DBG) log("ICC SIM AKA: auth_rsp = " + auth_rsp);
                    } catch (Exception e) {
                            rsp.authRsp = (IccIoResult) ar.result;
                            if (VDBG) log("ICC SIM AKA: authRsp = " + rsp.authRsp);
                        } catch (ClassCastException e) {
                            rsp.exception = e;
                            loge("Failed to parse ICC SIM AKA contents: " + e);
                        }
                    }
                synchronized (mLock) {
                    mLock.notifyAll();
                    rsp.notifyAll();
                }

                break;
@@ -1182,54 +1192,69 @@ public abstract class IccRecords extends Handler implements IccConstants {
    }

    /**
     * Solve authentication leakage issue. See b/147463955.
     * Returns the response of the SIM application on the UICC to authentication
     * challenge/response algorithm. The data string and challenge response are
     * Base64 encoded Strings.
     * Can support EAP-SIM, EAP-AKA with results encoded per 3GPP TS 31.102.
     *
     * @param authContext parameter P2 that specifies the authentication context per 3GPP TS 31.102 (Section 7.1.2)
     * @param authContext parameter P2 that specifies the authentication context
     * per 3GPP TS 31.102 (Section 7.1.2)
     * @param data authentication challenge data
     * @return challenge response
     */
    @UnsupportedAppUsage
    @Nullable
    public String getIccSimChallengeResponse(int authContext, String data) {
        if (DBG) log("getIccSimChallengeResponse:");
        if (VDBG) log("getIccSimChallengeResponse:");

        //final here is for defensive copy.
        final CommandsInterface ci = mCi;
        final UiccCardApplication parentApp = mParentApp;
        if (ci == null || parentApp == null) {
            loge("getIccSimChallengeResponse: Fail, ci or parentApp is null");
            return null;
        }

        AuthAsyncResponse rsp = new AuthAsyncResponse();

        synchronized (rsp) {
            ci.requestIccSimAuthentication(authContext, data, parentApp.getAid(),
                    obtainMessage(EVENT_AKA_AUTHENTICATE_DONE, 0, 0, rsp));
            //TODO: factor wait with timeout into a separate method
            final long startTime = SystemClock.elapsedRealtime();
            do {
                try {
            synchronized(mLock) {
                CommandsInterface ci = mCi;
                UiccCardApplication parentApp = mParentApp;
                if (ci != null && parentApp != null) {
                    ci.requestIccSimAuthentication(authContext, data,
                            parentApp.getAid(),
                            obtainMessage(EVENT_AKA_AUTHENTICATE_DONE));
                    try {
                        mLock.wait();
                    long sleepTime = startTime + ICC_SIM_CHALLENGE_TIMEOUT_MILLIS
                            - SystemClock.elapsedRealtime();
                    if (sleepTime > 0) rsp.wait(sleepTime);
                } catch (InterruptedException e) {
                        loge("getIccSimChallengeResponse: Fail, interrupted"
                                + " while trying to request Icc Sim Auth");
                        return null;
                    Rlog.w("IccRecords", "getIccSimChallengeResponse: InterruptedException.");
                }
                } else {
                    loge( "getIccSimChallengeResponse: "
                            + "Fail, ci or parentApp is null");
            } while (SystemClock.elapsedRealtime() - startTime < ICC_SIM_CHALLENGE_TIMEOUT_MILLIS
                    && rsp.authRsp == null && rsp.exception == null);

            if (SystemClock.elapsedRealtime() - startTime >= ICC_SIM_CHALLENGE_TIMEOUT_MILLIS
                    && rsp.authRsp == null && rsp.exception == null) {
                loge("getIccSimChallengeResponse timeout!");
                return null;
            }
            }
        } catch(Exception e) {
            loge( "getIccSimChallengeResponse: "
                    + "Fail while trying to request Icc Sim Auth");

            if (rsp.exception != null) {
                loge("getIccSimChallengeResponse exception: " + rsp.exception);
                //TODO: propagate better exceptions up to the user now that we have them available
                //in the call stack.
                return null;
            }

        if (auth_rsp == null) {
            if (rsp.authRsp == null) {
                loge("getIccSimChallengeResponse: No authentication response");
                return null;
            }
        }
        if (VDBG) log("getIccSimChallengeResponse: return rsp.authRsp");

        if (DBG) log("getIccSimChallengeResponse: return auth_rsp");

        return android.util.Base64.encodeToString(auth_rsp.payload, android.util.Base64.NO_WRAP);
        return android.util.Base64.encodeToString(rsp.authRsp.payload,
                android.util.Base64.NO_WRAP);
    }

    /**
+57 −17
Original line number Diff line number Diff line
@@ -79,7 +79,7 @@ import java.util.concurrent.atomic.AtomicInteger;

public class SimulatedCommands extends BaseCommands
        implements CommandsInterface, SimulatedRadioControl {
    private final static String LOG_TAG = "SimulatedCommands";
    private static final String LOG_TAG = "SimulatedCommands";

    private enum SimLockState {
        NONE,
@@ -95,21 +95,27 @@ public class SimulatedCommands extends BaseCommands
        SIM_PERM_LOCKED
    }

    private final static SimLockState INITIAL_LOCK_STATE = SimLockState.NONE;
    public final static String DEFAULT_SIM_PIN_CODE = "1234";
    private final static String SIM_PUK_CODE = "12345678";
    private final static SimFdnState INITIAL_FDN_STATE = SimFdnState.NONE;
    public final static String DEFAULT_SIM_PIN2_CODE = "5678";
    private final static String SIM_PUK2_CODE = "87654321";
    public final static String FAKE_LONG_NAME = "Fake long name";
    public final static String FAKE_SHORT_NAME = "Fake short name";
    public final static String FAKE_MCC_MNC = "310260";
    public final static String FAKE_IMEI = "012345678901234";
    public final static String FAKE_IMEISV = "99";
    public final static String FAKE_ESN = "1234";
    public final static String FAKE_MEID = "1234";
    public final static int DEFAULT_PIN1_ATTEMPT = 5;
    public final static int DEFAULT_PIN2_ATTEMPT = 5;
    private static final SimLockState INITIAL_LOCK_STATE = SimLockState.NONE;
    public static final String DEFAULT_SIM_PIN_CODE = "1234";
    private static final String SIM_PUK_CODE = "12345678";
    private static final SimFdnState INITIAL_FDN_STATE = SimFdnState.NONE;
    public static final String DEFAULT_SIM_PIN2_CODE = "5678";
    private static final String SIM_PUK2_CODE = "87654321";
    public static final String FAKE_LONG_NAME = "Fake long name";
    public static final String FAKE_SHORT_NAME = "Fake short name";
    public static final String FAKE_MCC_MNC = "310260";
    public static final String FAKE_IMEI = "012345678901234";
    public static final String FAKE_IMEISV = "99";
    public static final String FAKE_ESN = "1234";
    public static final String FAKE_MEID = "1234";
    public static final int DEFAULT_PIN1_ATTEMPT = 5;
    public static final int DEFAULT_PIN2_ATTEMPT = 5;
    public static final int ICC_AUTHENTICATION_MODE_DEFAULT = 0;
    public static final int ICC_AUTHENTICATION_MODE_NULL = 1;
    public static final int ICC_AUTHENTICATION_MODE_TIMEOUT = 2;
    // Maximum time in millisecond to wait for a IccSim Challenge before assuming it will not
    // arrive and returning null to the callers.
    public static final  long ICC_SIM_CHALLENGE_TIMEOUT_MILLIS = 2500;

    private String mImei;
    private String mImeiSv;
@@ -162,6 +168,8 @@ public class SimulatedCommands extends BaseCommands
    private SetupDataCallResult mSetupDataCallResult;
    private boolean mIsRadioPowerFailResponse = false;

    // mode for Icc Sim Authentication
    private int mAuthenticationMode;
    //***** Constructor
    public
    SimulatedCommands() {
@@ -179,6 +187,7 @@ public class SimulatedCommands extends BaseCommands
        mSimFdnEnabledState = INITIAL_FDN_STATE;
        mSimFdnEnabled = (mSimFdnEnabledState != SimFdnState.NONE);
        mPin2Code = DEFAULT_SIM_PIN2_CODE;
        mAuthenticationMode = ICC_AUTHENTICATION_MODE_DEFAULT;
    }

    public void dispose() {
@@ -1896,7 +1905,38 @@ public class SimulatedCommands extends BaseCommands

    @Override
    public void requestIccSimAuthentication(int authContext, String data, String aid, Message response) {
        unimplemented(response);
        switch (mAuthenticationMode) {
            case ICC_AUTHENTICATION_MODE_TIMEOUT:
                break;

            case ICC_AUTHENTICATION_MODE_NULL:
                sendMessageResponse(response, null);
                break;

            default:
                if (data == null || data.length() == 0) {
                    sendMessageResponse(response,  null);
                } else {
                    sendMessageResponse(response, new IccIoResult(0, 0, (byte[]) data.getBytes()));
                }
                break;
        }
    }

    /**
     * Helper function to send response msg
     * @param msg Response message to be sent
     * @param ret Return object to be included in the response message
     */
    private void sendMessageResponse(Message msg, Object ret) {
        if (msg != null) {
            AsyncResult.forMessage(msg, ret, null);
            msg.sendToTarget();
        }
    }

    public void setAuthenticationMode(int authenticationMode) {
        mAuthenticationMode = authenticationMode;
    }

    @Override
+94 −1
Original line number Diff line number Diff line
@@ -31,12 +31,14 @@ package com.android.internal.telephony.uicc;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;

import android.content.Context;
import android.os.AsyncResult;
import android.os.HandlerThread;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
import android.util.Pair;

import com.android.internal.telephony.TelephonyTest;
@@ -124,4 +126,95 @@ public class IccRecordsTest extends TelephonyTest {
        waitForLastHandlerAction(mIccRecords);
        assertEquals(mIccRecords.getSmsCapacityOnIcc(), 500);
    }

    @Test
    public void testGetIccSimChallengeResponseNull() {
        long startTime;
        long timeSpent;

        // EAP-SIM rand is 16 bytes.
        String base64Challenge = "ECcTqwuo6OfY8ddFRboD9WM=";

        // Test for null result
        mSimulatedCommands.setAuthenticationMode(mSimulatedCommands.ICC_AUTHENTICATION_MODE_NULL);

        startTime = SystemClock.elapsedRealtime();
        assertNull("getIccAuthentication should return null for empty data.",
                mIccRecords.getIccSimChallengeResponse(UiccCardApplication.AUTH_CONTEXT_EAP_AKA,
                      base64Challenge));
        timeSpent = SystemClock.elapsedRealtime() - startTime;
        Log.d("IccRecordsTest", "Time (ms) for getIccSimChallengeResponse is " + timeSpent);
        assertTrue("getIccAuthentication should not timeout",
                timeSpent < mSimulatedCommands.ICC_SIM_CHALLENGE_TIMEOUT_MILLIS);
    }

    @Test
    public void testGetIccSimChallengeResponseTimeout() {
        long startTime;
        long timeSpent;

        // EAP-SIM rand is 16 bytes.
        String base64Challenge = "ECcTqwuo6OfY8ddFRboD9WM=";

        mSimulatedCommands.setAuthenticationMode(
                mSimulatedCommands.ICC_AUTHENTICATION_MODE_TIMEOUT);
        startTime = SystemClock.elapsedRealtime();
        assertNull("getIccAuthentication should return null for empty data.",
                mIccRecords.getIccSimChallengeResponse(UiccCardApplication.AUTH_CONTEXT_EAP_AKA,
                      base64Challenge));
        timeSpent = SystemClock.elapsedRealtime() - startTime;
        Log.d("IccRecordsTest", "Time (ms) for getIccSimChallengeResponse is " + timeSpent);
        assertTrue("getIccAuthentication should timeout",
                timeSpent >= mSimulatedCommands.ICC_SIM_CHALLENGE_TIMEOUT_MILLIS);
    }

    @Test
    public void testGetIccSimChallengeResponseDefault() {
        long startTime;
        long timeSpent;

        // EAP-SIM rand is 16 bytes.
        String base64Challenge = "ECcTqwuo6OfY8ddFRboD9WM=";
        String base64Challenge2 = "EMNxjsFrPCpm+KcgCmQGnwQ=";

        // Test for default setup
        mSimulatedCommands.setAuthenticationMode(
                mSimulatedCommands.ICC_AUTHENTICATION_MODE_DEFAULT);

        // Test for null input
        startTime = SystemClock.elapsedRealtime();
        assertNull("getIccAuthentication should return null for empty data.",
                mIccRecords.getIccSimChallengeResponse(
                        UiccCardApplication.AUTH_CONTEXT_EAP_AKA, ""));
        timeSpent = SystemClock.elapsedRealtime() - startTime;
        Log.d("IccRecordsTest", "Time (ms) for getIccSimChallengeResponse is " + timeSpent);
        assertTrue("getIccAuthentication should not timeout",
                timeSpent < mSimulatedCommands.ICC_SIM_CHALLENGE_TIMEOUT_MILLIS);

        // EAP-SIM
        startTime = SystemClock.elapsedRealtime();
        String response = mIccRecords.getIccSimChallengeResponse(
                UiccCardApplication.AUTH_CONTEXT_EAP_SIM, base64Challenge);
        timeSpent = SystemClock.elapsedRealtime() - startTime;
        Log.d("IccRecordsTest", "Time (ms) for getIccSimChallengeResponse is " + timeSpent);
        Log.d("IccRecordsTest", "Result of getIccSimChallengeResponse is " + response);
        assertTrue("Response to EAP-SIM Challenge must not be Null.", response != null);

        startTime = SystemClock.elapsedRealtime();
        String response1 = mIccRecords.getIccSimChallengeResponse(
                UiccCardApplication.AUTH_CONTEXT_EAP_SIM, base64Challenge);
        timeSpent = SystemClock.elapsedRealtime() - startTime;
        Log.d("IccRecordsTest", "Time (ms) for getIccSimChallengeResponse is " + timeSpent);
        Log.d("IccRecordsTest", "Result of getIccSimChallengeResponse is " + response1);
        assertTrue("Response to EAP-SIM Challenge must be consistent.",
                response.equals(response1));

        startTime = SystemClock.elapsedRealtime();
        String response2 = mIccRecords.getIccSimChallengeResponse(
                UiccCardApplication.AUTH_CONTEXT_EAP_SIM, base64Challenge2);
        timeSpent = SystemClock.elapsedRealtime() - startTime;
        Log.d("IccRecordsTest", "Time (ms) for getIccSimChallengeResponse is " + timeSpent);
        assertTrue("Two responses must be different.", !response.equals(response2));
    }

}