diff options
author | Hai Shalom <haishalom@google.com> | 2020-04-09 19:10:06 -0700 |
---|---|---|
committer | Hai Shalom <haishalom@google.com> | 2020-04-13 16:25:48 -0700 |
commit | f17e56bbd9dd09169b9b4a6b9735acda9d31b9d1 (patch) | |
tree | 95c19375c554482d9cab891b86b0a362c1a781ff | |
parent | 6e5f940b37880a6d05a9a50c0dbb7245f2448ce0 (diff) |
[Passpoint] Add metrics for Root CA and Subscription Expiration
Add metrics for counting profiles with no Root CA, profiles
with self-signed Root CAs and profiles with Subscription Expiration.
Bug: 153695144
Test: atest PasspointManagerTest
Test: atest WifiMetricsTest
Change-Id: I94dd0a3a8948d612a3bccdcf6fcea677f93d97bf
5 files changed, 197 insertions, 0 deletions
diff --git a/service/java/com/android/server/wifi/WifiMetrics.java b/service/java/com/android/server/wifi/WifiMetrics.java index 0760dee4a..a9385ed91 100644 --- a/service/java/com/android/server/wifi/WifiMetrics.java +++ b/service/java/com/android/server/wifi/WifiMetrics.java @@ -2657,6 +2657,33 @@ public class WifiMetrics { } /** + * Increment number of Passpoint providers with no Root CA in their profile. + */ + public void incrementNumPasspointProviderWithNoRootCa() { + synchronized (mLock) { + mWifiLogProto.numPasspointProviderWithNoRootCa++; + } + } + + /** + * Increment number of Passpoint providers with a self-signed Root CA in their profile. + */ + public void incrementNumPasspointProviderWithSelfSignedRootCa() { + synchronized (mLock) { + mWifiLogProto.numPasspointProviderWithSelfSignedRootCa++; + } + } + + /** + * Increment number of Passpoint providers with subscription expiration date in their profile. + */ + public void incrementNumPasspointProviderWithSubscriptionExpiration() { + synchronized (mLock) { + mWifiLogProto.numPasspointProviderWithSubscriptionExpiration++; + } + } + + /** * Increment number of times we detected a radio mode change to MCC. */ public void incrementNumRadioModeChangeToMcc() { diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index a702c7e98..b60ef3f63 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -29,6 +29,8 @@ import static android.net.wifi.WifiManager.EXTRA_ICON; import static android.net.wifi.WifiManager.EXTRA_SUBSCRIPTION_REMEDIATION_METHOD; import static android.net.wifi.WifiManager.EXTRA_URL; +import static java.security.cert.PKIXReason.NO_TRUST_ANCHOR; + import android.annotation.NonNull; import android.app.AppOpsManager; import android.content.Context; @@ -65,7 +67,16 @@ import com.android.server.wifi.hotspot2.anqp.HSOsuProvidersElement; import com.android.server.wifi.hotspot2.anqp.OsuProviderInfo; import com.android.server.wifi.util.InformationElementUtil; +import java.io.IOException; import java.io.PrintWriter; +import java.security.GeneralSecurityException; +import java.security.KeyStore; +import java.security.cert.CertPath; +import java.security.cert.CertPathValidator; +import java.security.cert.CertPathValidatorException; +import java.security.cert.CertificateFactory; +import java.security.cert.PKIXParameters; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -439,6 +450,34 @@ public class PasspointManager { mWifiCarrierInfoManager, mProviderIndex++, uid, packageName, isFromSuggestion); newProvider.setTrusted(isTrusted); + boolean metricsNoRootCa = false; + boolean metricsSelfSignedRootCa = false; + boolean metricsSubscriptionExpiration = false; + + if (config.getCredential().getUserCredential() != null + || config.getCredential().getCertCredential() != null) { + X509Certificate[] x509Certificates = config.getCredential().getCaCertificates(); + if (x509Certificates == null) { + metricsNoRootCa = true; + } else { + try { + for (X509Certificate certificate : x509Certificates) { + verifyCaCert(certificate); + } + } catch (CertPathValidatorException e) { + // A self signed Root CA will fail path validation checks with NO_TRUST_ANCHOR + if (e.getReason() == NO_TRUST_ANCHOR) { + metricsSelfSignedRootCa = true; + } + } catch (Exception e) { + // Other exceptions, fall through, will be handled below + } + } + } + if (config.getSubscriptionExpirationTimeMillis() != Long.MIN_VALUE) { + metricsSubscriptionExpiration = true; + } + if (!newProvider.installCertsAndKeys()) { Log.e(TAG, "Failed to install certificates and keys to keystore"); return false; @@ -477,6 +516,15 @@ public class PasspointManager { Log.d(TAG, "Added/updated Passpoint configuration for FQDN: " + config.getHomeSp().getFqdn() + " with unique ID: " + config.getUniqueId() + " by UID: " + uid); + if (metricsNoRootCa) { + mWifiMetrics.incrementNumPasspointProviderWithNoRootCa(); + } + if (metricsSelfSignedRootCa) { + mWifiMetrics.incrementNumPasspointProviderWithSelfSignedRootCa(); + } + if (metricsSubscriptionExpiration) { + mWifiMetrics.incrementNumPasspointProviderWithSubscriptionExpiration(); + } mWifiMetrics.incrementNumPasspointProviderInstallSuccess(); return true; } @@ -1256,4 +1304,25 @@ public class PasspointManager { mAnqpRequestManager.clear(); mAnqpCache.flush(); } + + /** + * Verify that the given certificate is trusted by one of the pre-loaded public CAs in the + * system key store. + * + * @param caCert The CA Certificate to verify + * @throws CertPathValidatorException + * @throws Exception + */ + private void verifyCaCert(X509Certificate caCert) + throws GeneralSecurityException, IOException { + CertificateFactory factory = CertificateFactory.getInstance("X.509"); + CertPathValidator validator = + CertPathValidator.getInstance(CertPathValidator.getDefaultType()); + CertPath path = factory.generateCertPath(Arrays.asList(caCert)); + KeyStore ks = KeyStore.getInstance("AndroidCAStore"); + ks.load(null, null); + PKIXParameters params = new PKIXParameters(ks); + params.setRevocationEnabled(false); + validator.validate(path, params); + } } diff --git a/service/proto/src/metrics.proto b/service/proto/src/metrics.proto index 932fb2e0f..1f6848482 100644 --- a/service/proto/src/metrics.proto +++ b/service/proto/src/metrics.proto @@ -675,6 +675,15 @@ message WifiLog { // (SSID, BSSID) tuple depending on AP configuration (in the above priority // order). repeated NumConnectableNetworksBucket observed_hotspot_r3_ess_in_scan_histogram = 188; + + // Total number of Passpoint providers with no Root CA in their profile. + optional int32 num_passpoint_provider_with_no_root_ca = 189; + + // Total number of Passpoint providers with self-signed root CA in their profile. + optional int32 num_passpoint_provider_with_self_signed_root_ca = 190; + + // Total number of Passpoint providers with subscription expiration date in their profile. + optional int32 num_passpoint_provider_with_subscription_expiration = 191; } // Information that gets logged for every WiFi connection. diff --git a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java index c8b149ba6..3964d2d37 100644 --- a/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java +++ b/tests/wifitests/src/com/android/server/wifi/WifiMetricsTest.java @@ -398,6 +398,9 @@ public class WifiMetricsTest extends WifiBaseTest { private static final int NUM_PASSPOINT_PROVIDER_UNINSTALLATION = 3; private static final int NUM_PASSPOINT_PROVIDER_UNINSTALL_SUCCESS = 2; private static final int NUM_PASSPOINT_PROVIDERS_SUCCESSFULLY_CONNECTED = 1; + private static final int NUM_PASSPOINT_PROVIDERS_WITH_NO_ROOT_CA = 2; + private static final int NUM_PASSPOINT_PROVIDERS_WITH_SELF_SIGNED_ROOT_CA = 3; + private static final int NUM_PASSPOINT_PROVIDERS_WITH_EXPIRATION_DATE = 4; private static final int NUM_EAP_SIM_TYPE = 1; private static final int NUM_EAP_TTLS_TYPE = 2; private static final int NUM_EAP_TLS_TYPE = 3; @@ -873,6 +876,15 @@ public class WifiMetricsTest extends WifiBaseTest { for (int i = 0; i < NUM_PASSPOINT_PROVIDER_UNINSTALL_SUCCESS; i++) { mWifiMetrics.incrementNumPasspointProviderUninstallSuccess(); } + for (int i = 0; i < NUM_PASSPOINT_PROVIDERS_WITH_NO_ROOT_CA; i++) { + mWifiMetrics.incrementNumPasspointProviderWithNoRootCa(); + } + for (int i = 0; i < NUM_PASSPOINT_PROVIDERS_WITH_SELF_SIGNED_ROOT_CA; i++) { + mWifiMetrics.incrementNumPasspointProviderWithSelfSignedRootCa(); + } + for (int i = 0; i < NUM_PASSPOINT_PROVIDERS_WITH_EXPIRATION_DATE; i++) { + mWifiMetrics.incrementNumPasspointProviderWithSubscriptionExpiration(); + } for (int i = 0; i < NUM_RADIO_MODE_CHANGE_TO_MCC; i++) { mWifiMetrics.incrementNumRadioModeChangeToMcc(); } @@ -1329,6 +1341,13 @@ public class WifiMetricsTest extends WifiBaseTest { mDecodedProto.numPasspointProviderUninstallSuccess); assertEquals(NUM_PASSPOINT_PROVIDERS_SUCCESSFULLY_CONNECTED, mDecodedProto.numPasspointProvidersSuccessfullyConnected); + assertEquals(NUM_PASSPOINT_PROVIDERS_WITH_NO_ROOT_CA, + mDecodedProto.numPasspointProviderWithNoRootCa); + assertEquals(NUM_PASSPOINT_PROVIDERS_WITH_SELF_SIGNED_ROOT_CA, + mDecodedProto.numPasspointProviderWithSelfSignedRootCa); + assertEquals(NUM_PASSPOINT_PROVIDERS_WITH_EXPIRATION_DATE, + mDecodedProto.numPasspointProviderWithSubscriptionExpiration); + assertEquals(NUM_RADIO_MODE_CHANGE_TO_MCC, mDecodedProto.numRadioModeChangeToMcc); assertEquals(NUM_RADIO_MODE_CHANGE_TO_SCC, mDecodedProto.numRadioModeChangeToScc); assertEquals(NUM_RADIO_MODE_CHANGE_TO_SBS, mDecodedProto.numRadioModeChangeToSbs); diff --git a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java index 73c5713ac..861aa94ff 100644 --- a/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java +++ b/tests/wifitests/src/com/android/server/wifi/hotspot2/PasspointManagerTest.java @@ -2560,4 +2560,77 @@ public class PasspointManagerTest extends WifiBaseTest { // Verify content in the data source. assertTrue(mUserDataSource.getProviders().isEmpty()); } + + /** + * Verify that adding a provider with a self signed root CA increments the metrics correctly. + * + * @throws Exception + */ + @Test + public void verifySelfSignRootCaMetrics() throws Exception { + WifiConfiguration wifiConfig = new WifiConfiguration(); + wifiConfig.FQDN = TEST_FQDN; + PasspointConfiguration config = + createTestConfigWithUserCredential(TEST_FQDN, TEST_FRIENDLY_NAME); + PasspointProvider provider = createMockProvider(config, wifiConfig, true); + when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), + eq(mWifiCarrierInfoManager), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(true))).thenReturn(provider); + when(provider.getPackageName()).thenReturn(TEST_PACKAGE); + assertTrue(mManager.addOrUpdateProvider( + config, TEST_CREATOR_UID, TEST_PACKAGE, true, false)); + verify(mWifiMetrics).incrementNumPasspointProviderWithSelfSignedRootCa(); + verify(mWifiMetrics, never()).incrementNumPasspointProviderWithNoRootCa(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + } + + /** + * Verify that adding a provider with no root CA increments the metrics correctly. + * + * @throws Exception + */ + @Test + public void verifyNoRootCaMetrics() throws Exception { + WifiConfiguration wifiConfig = new WifiConfiguration(); + wifiConfig.FQDN = TEST_FQDN; + PasspointConfiguration config = + createTestConfigWithUserCredential(TEST_FQDN, TEST_FRIENDLY_NAME); + config.getCredential().setCaCertificate(null); + PasspointProvider provider = createMockProvider(config, wifiConfig, true); + when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), + eq(mWifiCarrierInfoManager), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(true))).thenReturn(provider); + when(provider.getPackageName()).thenReturn(TEST_PACKAGE); + assertTrue(mManager.addOrUpdateProvider( + config, TEST_CREATOR_UID, TEST_PACKAGE, true, false)); + verify(mWifiMetrics).incrementNumPasspointProviderWithNoRootCa(); + verify(mWifiMetrics, never()).incrementNumPasspointProviderWithSelfSignedRootCa(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + } + + /** + * Verify that adding a provider with subscription expiration increments the metrics correctly. + * + * @throws Exception + */ + @Test + public void verifySubscriptionExpirationMetrics() throws Exception { + WifiConfiguration wifiConfig = new WifiConfiguration(); + wifiConfig.FQDN = TEST_FQDN; + PasspointConfiguration config = + createTestConfigWithUserCredential(TEST_FQDN, TEST_FRIENDLY_NAME); + config.setSubscriptionExpirationTimeInMillis(1586228641000L); + PasspointProvider provider = createMockProvider(config, wifiConfig, true); + when(mObjectFactory.makePasspointProvider(eq(config), eq(mWifiKeyStore), + eq(mWifiCarrierInfoManager), anyLong(), eq(TEST_CREATOR_UID), eq(TEST_PACKAGE), + eq(true))).thenReturn(provider); + when(provider.getPackageName()).thenReturn(TEST_PACKAGE); + assertTrue(mManager.addOrUpdateProvider( + config, TEST_CREATOR_UID, TEST_PACKAGE, true, false)); + verify(mWifiMetrics).incrementNumPasspointProviderWithSubscriptionExpiration(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallation(); + verify(mWifiMetrics).incrementNumPasspointProviderInstallSuccess(); + } } |