From bc8fa0c163c40afa999ac71cc78687bb845131ab Mon Sep 17 00:00:00 2001 From: Hai Shalom Date: Mon, 23 Sep 2019 14:36:13 -0700 Subject: [EAP-SIM] Add NAI realm decoration to pseudonym The framework stores the last pseudonym given by the server. However, in some cases, it does not contain @ suffix which some servers require. Check if NAI realm exist, and add @ to the pseudonym for subsequent connections if missing. Bug: 109795427 Test: atest ClientModeImplTest Test: Regression test in lab passed b/143503329 Merged-In: I74ef92e05e76290cbbf96297ed3329426e8b95a3 Change-Id: I74ef92e05e76290cbbf96297ed3329426e8b95a3 (cherry picked from commit 97447d4b970bf7e569d10dc6ca5829d99e9a4e44) --- .../com/android/server/wifi/ClientModeImpl.java | 21 +++++-- .../android/server/wifi/util/TelephonyUtil.java | 40 ++++++++++++++ .../android/server/wifi/ClientModeImplTest.java | 64 ++++++++++++++++++++-- 3 files changed, 114 insertions(+), 11 deletions(-) diff --git a/service/java/com/android/server/wifi/ClientModeImpl.java b/service/java/com/android/server/wifi/ClientModeImpl.java index 7e7f651de..119d6d0e8 100644 --- a/service/java/com/android/server/wifi/ClientModeImpl.java +++ b/service/java/com/android/server/wifi/ClientModeImpl.java @@ -4473,14 +4473,25 @@ public class ClientModeImpl extends StateMachine { config.enterpriseConfig.getEapMethod())) { String anonymousIdentity = mWifiNative.getEapAnonymousIdentity(mInterfaceName); - if (mVerboseLoggingEnabled) { - log("EAP Pseudonym: " + anonymousIdentity); - } - if (!TelephonyUtil.isAnonymousAtRealmIdentity(anonymousIdentity)) { + if (!TextUtils.isEmpty(anonymousIdentity) + && !TelephonyUtil + .isAnonymousAtRealmIdentity(anonymousIdentity)) { + String decoratedPseudonym = TelephonyUtil + .decoratePseudonymWith3GppRealm(getTelephonyManager(), + anonymousIdentity); + if (decoratedPseudonym != null) { + anonymousIdentity = decoratedPseudonym; + } + if (mVerboseLoggingEnabled) { + log("EAP Pseudonym: " + anonymousIdentity); + } // Save the pseudonym only if it is a real one config.enterpriseConfig.setAnonymousIdentity(anonymousIdentity); - mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); + } else { + // Clear any stored pseudonyms + config.enterpriseConfig.setAnonymousIdentity(null); } + mWifiConfigManager.addOrUpdateNetwork(config, Process.WIFI_UID); } sendNetworkStateChangeBroadcast(mLastBssid); transitionTo(mObtainingIpState); diff --git a/service/java/com/android/server/wifi/util/TelephonyUtil.java b/service/java/com/android/server/wifi/util/TelephonyUtil.java index 4af40ddf2..3154df978 100644 --- a/service/java/com/android/server/wifi/util/TelephonyUtil.java +++ b/service/java/com/android/server/wifi/util/TelephonyUtil.java @@ -22,6 +22,7 @@ import android.net.wifi.WifiEnterpriseConfig; import android.telephony.ImsiEncryptionInfo; import android.telephony.SubscriptionManager; import android.telephony.TelephonyManager; +import android.text.TextUtils; import android.util.Base64; import android.util.Log; import android.util.Pair; @@ -730,4 +731,43 @@ public class TelephonyUtil { public static boolean isSimPresent(@Nonnull SubscriptionManager sm) { return sm.getActiveSubscriptionIdList().length > 0; } + + /** + * Decorates a pseudonym with the NAI realm, in case it wasn't provided by the server + * + * @param tm TelephonyManager instance + * @param pseudonym The pseudonym (temporary identity) provided by the server + * @return pseudonym@realm which is based on current MCC/MNC, {@code null} if SIM is + * not ready or absent. + */ + public static String decoratePseudonymWith3GppRealm(@NonNull TelephonyManager tm, + String pseudonym) { + if (tm == null || TextUtils.isEmpty(pseudonym)) { + return null; + } + if (pseudonym.contains("@")) { + // Pseudonym is already decorated + return pseudonym; + } + TelephonyManager defaultDataTm = tm.createForSubscriptionId( + SubscriptionManager.getDefaultDataSubscriptionId()); + if (defaultDataTm.getSimState() != TelephonyManager.SIM_STATE_READY) { + return null; + } + String mccMnc = defaultDataTm.getSimOperator(); + if (mccMnc == null || mccMnc.isEmpty()) { + return null; + } + + // Extract mcc & mnc from mccMnc + String mcc = mccMnc.substring(0, 3); + String mnc = mccMnc.substring(3); + + if (mnc.length() == 2) { + mnc = "0" + mnc; + } + + String realm = String.format(THREE_GPP_NAI_REALM_FORMAT, mnc, mcc); + return String.format("%s@%s", pseudonym, realm); + } } diff --git a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java index c3e31131d..9eb88b637 100644 --- a/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java +++ b/tests/wifitests/src/com/android/server/wifi/ClientModeImplTest.java @@ -1047,6 +1047,9 @@ public class ClientModeImplTest { when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); + // Initial value should be "not set" + assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); + triggerConnect(); // CMD_START_CONNECT should have set anonymousIdentity to anonymous@ @@ -1066,15 +1069,15 @@ public class ClientModeImplTest { mLooper.dispatchAll(); verify(mWifiNative).getEapAnonymousIdentity(any()); - // check that the anonymous identity remains anonymous@ for subsequent connections. - assertEquals(expectedAnonymousIdentity, - mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); - // verify that WifiConfigManager#addOrUpdateNetwork() was never called if there is no - // real pseudonym to be stored. i.e. Encrypted IMSI will be always used + + // Post connection value should remain "not set" + assertEquals("", mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); + // verify that WifiConfigManager#addOrUpdateNetwork() was called to clear any previously + // stored pseudonym. i.e. to enable Encrypted IMSI for subsequent connections. // Note: This test will fail if future logic will have additional conditions that would // trigger "add or update network" operation. The test needs to be updated to account for // this change. - verify(mWifiConfigManager, never()).addOrUpdateNetwork(any(), anyInt()); + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt()); } /** @@ -1126,6 +1129,55 @@ public class ClientModeImplTest { verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt()); } + /** + * Tests anonymous identity is set again whenever a connection is established for the carrier + * that supports encrypted IMSI and anonymous identity but real but not decorated pseudonym was + * provided for subsequent connections. + */ + @Test + public void testSetAnonymousIdentityWhenConnectionIsEstablishedWithNonDecoratedPseudonym() + throws Exception { + mConnectedNetwork = spy(WifiConfigurationTestUtil.createEapNetwork( + WifiEnterpriseConfig.Eap.SIM, WifiEnterpriseConfig.Phase2.NONE)); + when(mDataTelephonyManager.getSimOperator()).thenReturn("123456"); + when(mDataTelephonyManager.getSimState()).thenReturn(TelephonyManager.SIM_STATE_READY); + mConnectedNetwork.enterpriseConfig.setAnonymousIdentity(""); + + String realm = "wlan.mnc456.mcc123.3gppnetwork.org"; + String expectedAnonymousIdentity = "anonymous"; + String pseudonym = "83bcca9384fca"; + + when(mCarrierNetworkConfig.isCarrierEncryptionInfoAvailable()).thenReturn(true); + + triggerConnect(); + + // CMD_START_CONNECT should have set anonymousIdentity to anonymous@ + assertEquals(expectedAnonymousIdentity + "@" + realm, + mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); + + when(mWifiConfigManager.getScanDetailCacheForNetwork(FRAMEWORK_NETWORK_ID)) + .thenReturn(mScanDetailCache); + when(mScanDetailCache.getScanDetail(sBSSID)).thenReturn( + getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq)); + when(mScanDetailCache.getScanResult(sBSSID)).thenReturn( + getGoogleGuestScanDetail(TEST_RSSI, sBSSID, sFreq).getScanResult()); + when(mWifiNative.getEapAnonymousIdentity(anyString())) + .thenReturn(pseudonym); + + mCmi.sendMessage(WifiMonitor.NETWORK_CONNECTION_EVENT, 0, 0, sBSSID); + mLooper.dispatchAll(); + + verify(mWifiNative).getEapAnonymousIdentity(any()); + assertEquals(pseudonym + "@" + realm, + mConnectedNetwork.enterpriseConfig.getAnonymousIdentity()); + // Verify that WifiConfigManager#addOrUpdateNetwork() was called if there we received a + // real pseudonym to be stored. i.e. Encrypted IMSI will be used once, followed by + // pseudonym usage in all subsequent connections. + // Note: This test will fail if future logic will have additional conditions that would + // trigger "add or update network" operation. The test needs to be updated to account for + // this change. + verify(mWifiConfigManager).addOrUpdateNetwork(any(), anyInt()); + } /** * Tests the Passpoint information is set in WifiInfo for Passpoint AP connection. */ -- cgit v1.2.3