From 4354285b8de3345d5146f68ce2d17be83b4f34a4 Mon Sep 17 00:00:00 2001 From: Hai Shalom Date: Wed, 11 Mar 2020 13:57:36 -0700 Subject: [Passpoint] Fix Passpoint matching algorithm for Home networks Match Home networks if there is an FQDN match (without realm or method), or if OtherHomePartners element exists and matches the other advertised FQDNs, or if HomeOIList element exists and matches advertised RCOIs. Bug: 151245024 Test: atest PasspointProviderTest ANQPMatcherTest Change-Id: I7bf9c98c853c3973d30ce2651bdea49546dae4b5 Merged-In: I513f83a6d545b9ae7da5577ee163ce7d186f006e --- .../android/server/wifi/hotspot2/ANQPMatcher.java | 63 ++------ .../server/wifi/hotspot2/PasspointManager.java | 6 + .../server/wifi/hotspot2/PasspointProvider.java | 174 +++++++++++++++++---- 3 files changed, 165 insertions(+), 78 deletions(-) (limited to 'service') diff --git a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java index d95ab3831..69f98b0ef 100644 --- a/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java +++ b/service/java/com/android/server/wifi/hotspot2/ANQPMatcher.java @@ -75,10 +75,11 @@ public class ANQPMatcher { * * @param element The Roaming Consortium ANQP element * @param providerOIs The roaming consortium OIs of the provider + * @param matchAll Indicates if a match with all OIs must be done * @return true if a match is found */ public static boolean matchRoamingConsortium(RoamingConsortiumElement element, - long[] providerOIs) { + long[] providerOIs, boolean matchAll) { if (element == null) { return false; } @@ -88,10 +89,14 @@ public class ANQPMatcher { List rcOIs = element.getOIs(); for (long oi : providerOIs) { if (rcOIs.contains(oi)) { - return true; + if (!matchAll) { + return true; + } + } else if (matchAll) { + return false; } } - return false; + return matchAll; } /** @@ -100,27 +105,19 @@ public class ANQPMatcher { * * @param element The NAI Realm ANQP element * @param realm The realm of the provider's credential - * @param eapMethodID The EAP Method ID of the provider's credential - * @param authParam The authentication parameter of the provider's credential * @return an integer indicating the match status */ - public static int matchNAIRealm(NAIRealmElement element, String realm, int eapMethodID, - AuthParam authParam) { + public static boolean matchNAIRealm(NAIRealmElement element, String realm) { if (element == null || element.getRealmDataList().isEmpty()) { - return AuthMatch.INDETERMINATE; + return false; } - int bestMatch = AuthMatch.NONE; for (NAIRealmData realmData : element.getRealmDataList()) { - int match = matchNAIRealmData(realmData, realm, eapMethodID, authParam); - if (match > bestMatch) { - bestMatch = match; - if (bestMatch == AuthMatch.EXACT) { - break; - } + if (matchNAIRealmData(realmData, realm)) { + return true; } } - return bestMatch; + return false; } /** @@ -172,42 +169,16 @@ public class ANQPMatcher { * * @param realmData The NAI Realm data * @param realm The realm of the provider's credential - * @param eapMethodID The EAP Method ID of the provider's credential - * @param authParam The authentication parameter of the provider's credential - * @return an integer indicating the match status + * @return true if a match is found */ - private static int matchNAIRealmData(NAIRealmData realmData, String realm, int eapMethodID, - AuthParam authParam) { + private static boolean matchNAIRealmData(NAIRealmData realmData, String realm) { // Check for realm domain name match. - int realmMatch = AuthMatch.NONE; for (String realmStr : realmData.getRealms()) { if (DomainMatcher.arg2SubdomainOfArg1(realm, realmStr)) { - realmMatch = AuthMatch.REALM; - break; - } - } - - if (realmData.getEAPMethods().isEmpty()) { - return realmMatch; - } - - // Check for EAP method match. - int eapMethodMatch = AuthMatch.NONE; - for (EAPMethod eapMethod : realmData.getEAPMethods()) { - eapMethodMatch = matchEAPMethod(eapMethod, eapMethodID, authParam); - if (eapMethodMatch != AuthMatch.NONE) { - break; + return true; } } - - if (eapMethodMatch == AuthMatch.NONE) { - return AuthMatch.NONE; - } - - if (realmMatch == AuthMatch.NONE) { - return eapMethodMatch; - } - return realmMatch | eapMethodMatch; + return false; } private static int getEapMethodForNAIRealmWithCarrier(String realm, diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java index c46873761..172d1a13d 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointManager.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointManager.java @@ -217,6 +217,7 @@ public class PasspointManager { public void setProviders(List providers) { mProviders.clear(); for (PasspointProvider provider : providers) { + provider.enableVerboseLogging(mVerboseLoggingEnabled ? 1 : 0); mProviders.put(provider.getConfig().getHomeSp().getFqdn(), provider); if (provider.getPackageName() != null) { startTrackingAppOpsChange(provider.getPackageName(), @@ -377,6 +378,9 @@ public class PasspointManager { public void enableVerboseLogging(int verbose) { mVerboseLoggingEnabled = (verbose > 0) ? true : false; mPasspointProvisioner.enableVerboseLogging(verbose); + for (PasspointProvider provider : mProviders.values()) { + provider.enableVerboseLogging(verbose); + } } /** @@ -433,6 +437,7 @@ public class PasspointManager { mProviders.get(config.getHomeSp().getFqdn()).uninstallCertsAndKeys(); mProviders.remove(config.getHomeSp().getFqdn()); } + newProvider.enableVerboseLogging(mVerboseLoggingEnabled ? 1 : 0); mProviders.put(config.getHomeSp().getFqdn(), newProvider); mWifiConfigManager.saveToStore(true /* forceWrite */); if (newProvider.getPackageName() != null) { @@ -1165,6 +1170,7 @@ public class PasspointManager { Arrays.asList(enterpriseConfig.getCaCertificateAlias()), enterpriseConfig.getClientCertificateAlias(), enterpriseConfig.getClientCertificateAlias(), null, false, false); + provider.enableVerboseLogging(mVerboseLoggingEnabled ? 1 : 0); mProviders.put(passpointConfig.getHomeSp().getFqdn(), provider); return true; } diff --git a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java index ca9814aa6..8db71d3e6 100644 --- a/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java +++ b/service/java/com/android/server/wifi/hotspot2/PasspointProvider.java @@ -95,6 +95,7 @@ public class PasspointProvider { private boolean mHasEverConnected; private boolean mIsShared; + private boolean mVerboseLoggingEnabled; /** * This is a flag to indicate if the Provider is created temporarily. @@ -327,39 +328,53 @@ public class PasspointProvider { * Return the matching status with the given AP, based on the ANQP elements from the AP. * * @param anqpElements ANQP elements from the AP - * @param roamingConsortium Roaming Consortium information element from the AP + * @param roamingConsortiumFromAp Roaming Consortium information element from the AP * @return {@link PasspointMatch} */ public PasspointMatch match(Map anqpElements, - RoamingConsortium roamingConsortium) { - PasspointMatch providerMatch = matchProviderExceptFor3GPP(anqpElements, roamingConsortium); + RoamingConsortium roamingConsortiumFromAp) { + // Match FQDN for Home provider or RCOI(s) for Roaming provider + // For SIM credential, the FQDN is in the format of wlan.mnc*.mcc*.3gppnetwork.org + PasspointMatch providerMatch = matchFqdnAndRcoi(anqpElements, roamingConsortiumFromAp); // 3GPP Network matching. if (providerMatch == PasspointMatch.None && ANQPMatcher.matchThreeGPPNetwork( (ThreeGPPNetworkElement) anqpElements.get(ANQPElementType.ANQP3GPPNetwork), mImsiParameter, mMatchingSIMImsiList)) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Final RoamingProvider match with " + + anqpElements.get(ANQPElementType.ANQP3GPPNetwork)); + } return PasspointMatch.RoamingProvider; } - // Perform authentication match against the NAI Realm. - int authMatch = ANQPMatcher.matchNAIRealm( + // Perform NAI Realm matching + boolean realmMatch = ANQPMatcher.matchNAIRealm( (NAIRealmElement) anqpElements.get(ANQPElementType.ANQPNAIRealm), - mConfig.getCredential().getRealm(), mEAPMethodID, mAuthParam); - - // In case of Auth mismatch, demote provider match. - if (authMatch == AuthMatch.NONE) { - return PasspointMatch.None; - } + mConfig.getCredential().getRealm()); // In case of no realm match, return provider match as is. - if ((authMatch & AuthMatch.REALM) == 0) { + if (!realmMatch) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "No NAI realm match, final match: " + providerMatch); + } return providerMatch; } - // Promote the provider match to roaming provider if provider match is not found, but NAI + if (mVerboseLoggingEnabled) { + Log.d(TAG, "NAI realm match with " + mConfig.getCredential().getRealm()); + } + + // Promote the provider match to RoamingProvider if provider match is not found, but NAI // realm is matched. - return providerMatch == PasspointMatch.None ? PasspointMatch.RoamingProvider - : providerMatch; + if (providerMatch == PasspointMatch.None) { + providerMatch = PasspointMatch.RoamingProvider; + } + + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Final match: " + providerMatch); + } + return providerMatch; } /** @@ -569,43 +584,130 @@ public class PasspointProvider { || passpointConfig.getUsageLimitTimeLimitInMinutes() > 0; } + /** + * Match given OIs to the Roaming Consortium OIs + * + * @param providerOis Provider OIs to match against + * @param roamingConsortiumElement RCOIs in the ANQP element + * @param roamingConsortiumFromAp RCOIs in the AP scan results + * @param matchAll Indicates if all providerOis must match the RCOIs elements + * @return {@code true} if there is a match, {@code false} otherwise. + */ + private boolean matchOis(long[] providerOis, + RoamingConsortiumElement roamingConsortiumElement, + RoamingConsortium roamingConsortiumFromAp, + boolean matchAll) { + + + // ANQP Roaming Consortium OI matching. + if (ANQPMatcher.matchRoamingConsortium(roamingConsortiumElement, providerOis, matchAll)) { + if (mVerboseLoggingEnabled) { + Log.e(TAG, "ANQP RCOI match " + roamingConsortiumElement); + } + return true; + } + + // AP Roaming Consortium OI matching. + long[] apRoamingConsortiums = roamingConsortiumFromAp.getRoamingConsortiums(); + if (apRoamingConsortiums == null || providerOis == null) { + return false; + } + // Roaming Consortium OI information element matching. + for (long apOi: apRoamingConsortiums) { + boolean matched = false; + for (long providerOi: providerOis) { + if (apOi == providerOi) { + if (mVerboseLoggingEnabled) { + Log.e(TAG, "AP RCOI match: " + apOi); + } + if (!matchAll) { + return true; + } else { + matched = true; + break; + } + } + } + if (matchAll && !matched) { + return false; + } + } + return matchAll; + } + /** * Perform a provider match based on the given ANQP elements except for matching 3GPP Network. * * @param anqpElements List of ANQP elements - * @param roamingConsortium Roaming Consortium information element from the AP + * @param roamingConsortiumFromAp Roaming Consortium information element from the AP * @return {@link PasspointMatch} */ - private PasspointMatch matchProviderExceptFor3GPP( + private PasspointMatch matchFqdnAndRcoi( Map anqpElements, - RoamingConsortium roamingConsortium) { + RoamingConsortium roamingConsortiumFromAp) { // Domain name matching. if (ANQPMatcher.matchDomainName( (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName), mConfig.getHomeSp().getFqdn(), mImsiParameter, mMatchingSIMImsiList)) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Domain name " + mConfig.getHomeSp().getFqdn() + + " match: HomeProvider"); + } return PasspointMatch.HomeProvider; } - // ANQP Roaming Consortium OI matching. - long[] providerOIs = mConfig.getHomeSp().getRoamingConsortiumOis(); - if (ANQPMatcher.matchRoamingConsortium( - (RoamingConsortiumElement) anqpElements.get(ANQPElementType.ANQPRoamingConsortium), - providerOIs)) { - return PasspointMatch.RoamingProvider; + // Other Home Partners matching. + if (mConfig.getHomeSp().getOtherHomePartners() != null) { + for (String otherHomePartner : mConfig.getHomeSp().getOtherHomePartners()) { + if (ANQPMatcher.matchDomainName( + (DomainNameElement) anqpElements.get(ANQPElementType.ANQPDomName), + otherHomePartner, null, null)) { + if (mVerboseLoggingEnabled) { + Log.d(TAG, "Other Home Partner " + otherHomePartner + + " match: HomeProvider"); + } + return PasspointMatch.HomeProvider; + } + } } - long[] roamingConsortiums = roamingConsortium.getRoamingConsortiums(); - // Roaming Consortium OI information element matching. - if (roamingConsortiums != null && providerOIs != null) { - for (long sta_oi: roamingConsortiums) { - for (long ap_oi: providerOIs) { - if (sta_oi == ap_oi) { - return PasspointMatch.RoamingProvider; - } + // HomeOI matching + if (mConfig.getHomeSp().getMatchAllOis() != null) { + // Ensure that every HomeOI whose corresponding HomeOIRequired value is true shall match + // an OI in the Roaming Consortium advertised by the hotspot operator. + if (matchOis(mConfig.getHomeSp().getMatchAllOis(), (RoamingConsortiumElement) + anqpElements.get(ANQPElementType.ANQPRoamingConsortium), + roamingConsortiumFromAp, true)) { + if (mVerboseLoggingEnabled) { + Log.e(TAG, "All HomeOI RCOI match: HomeProvider"); + } + return PasspointMatch.HomeProvider; + } + } else if (mConfig.getHomeSp().getMatchAnyOis() != null) { + // Ensure that any HomeOI whose corresponding HomeOIRequired value is false shall match + // an OI in the Roaming Consortium advertised by the hotspot operator. + if (matchOis(mConfig.getHomeSp().getMatchAnyOis(), (RoamingConsortiumElement) + anqpElements.get(ANQPElementType.ANQPRoamingConsortium), + roamingConsortiumFromAp, false)) { + if (mVerboseLoggingEnabled) { + Log.e(TAG, "Any HomeOI RCOI match: HomeProvider"); } + return PasspointMatch.HomeProvider; } } + // Roaming Consortium OI matching. + if (matchOis(mConfig.getHomeSp().getRoamingConsortiumOis(), (RoamingConsortiumElement) + anqpElements.get(ANQPElementType.ANQPRoamingConsortium), + roamingConsortiumFromAp, false)) { + if (mVerboseLoggingEnabled) { + Log.e(TAG, "ANQP RCOI match: RoamingProvider"); + } + return PasspointMatch.RoamingProvider; + } + if (mVerboseLoggingEnabled) { + Log.e(TAG, "No domain name or RCOI match"); + } return PasspointMatch.None; } @@ -768,4 +870,12 @@ public class PasspointProvider { simCredential.setEapType(eapType); return simCredential; } + + /** + * Enable verbose logging + * @param verbose more than 0 enables verbose logging + */ + public void enableVerboseLogging(int verbose) { + mVerboseLoggingEnabled = (verbose > 0) ? true : false; + } } -- cgit v1.2.3