1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wifi.hotspot2; 18 19 import static com.android.server.wifi.hotspot2.Utils.isCarrierEapMethod; 20 21 import com.android.server.wifi.IMSIParameter; 22 import com.android.server.wifi.hotspot2.anqp.CellularNetwork; 23 import com.android.server.wifi.hotspot2.anqp.DomainNameElement; 24 import com.android.server.wifi.hotspot2.anqp.NAIRealmData; 25 import com.android.server.wifi.hotspot2.anqp.NAIRealmElement; 26 import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement; 27 import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement; 28 import com.android.server.wifi.hotspot2.anqp.eap.AuthParam; 29 import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod; 30 31 import java.util.List; 32 import java.util.Map; 33 import java.util.Set; 34 35 /** 36 * Utility class for providing matching functions against ANQP elements. 37 */ 38 public class ANQPMatcher { 39 /** 40 * Match the domain names in the ANQP element against the provider's FQDN and SIM credential. 41 * The Domain Name ANQP element might contain domains for 3GPP network (e.g. 42 * wlan.mnc*.mcc*.3gppnetwork.org), so we should match that against the provider's SIM 43 * credential if one is provided. 44 * 45 * @param element The Domain Name ANQP element 46 * @param fqdn The FQDN to compare against 47 * @param imsiParam The IMSI parameter of the provider 48 * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's 49 * IMSI parameter 50 * @return true if a match is found 51 */ matchDomainName(DomainNameElement element, String fqdn, IMSIParameter imsiParam, List<String> simImsiList)52 public static boolean matchDomainName(DomainNameElement element, String fqdn, 53 IMSIParameter imsiParam, List<String> simImsiList) { 54 if (element == null) { 55 return false; 56 } 57 58 for (String domain : element.getDomains()) { 59 if (DomainMatcher.arg2SubdomainOfArg1(fqdn, domain)) { 60 return true; 61 } 62 63 // Try to retrieve the MCC-MNC string from the domain (for 3GPP network domain) and 64 // match against the provider's SIM credential. 65 if (matchMccMnc(Utils.getMccMnc(Utils.splitDomain(domain)), imsiParam, simImsiList)) { 66 return true; 67 } 68 } 69 return false; 70 } 71 72 /** 73 * Match the roaming consortium OIs in the ANQP element against the roaming consortium OIs 74 * of a provider. 75 * 76 * @param element The Roaming Consortium ANQP element 77 * @param providerOIs The roaming consortium OIs of the provider 78 * @return true if a match is found 79 */ matchRoamingConsortium(RoamingConsortiumElement element, long[] providerOIs)80 public static boolean matchRoamingConsortium(RoamingConsortiumElement element, 81 long[] providerOIs) { 82 if (element == null) { 83 return false; 84 } 85 if (providerOIs == null) { 86 return false; 87 } 88 List<Long> rcOIs = element.getOIs(); 89 for (long oi : providerOIs) { 90 if (rcOIs.contains(oi)) { 91 return true; 92 } 93 } 94 return false; 95 } 96 97 /** 98 * Match the NAI realm in the ANQP element against the realm and authentication method of 99 * a provider. 100 * 101 * @param element The NAI Realm ANQP element 102 * @param realm The realm of the provider's credential 103 * @param eapMethodID The EAP Method ID of the provider's credential 104 * @param authParam The authentication parameter of the provider's credential 105 * @return an integer indicating the match status 106 */ matchNAIRealm(NAIRealmElement element, String realm, int eapMethodID, AuthParam authParam)107 public static int matchNAIRealm(NAIRealmElement element, String realm, int eapMethodID, 108 AuthParam authParam) { 109 if (element == null || element.getRealmDataList().isEmpty()) { 110 return AuthMatch.INDETERMINATE; 111 } 112 113 int bestMatch = AuthMatch.NONE; 114 for (NAIRealmData realmData : element.getRealmDataList()) { 115 int match = matchNAIRealmData(realmData, realm, eapMethodID, authParam); 116 if (match > bestMatch) { 117 bestMatch = match; 118 if (bestMatch == AuthMatch.EXACT) { 119 break; 120 } 121 } 122 } 123 return bestMatch; 124 } 125 126 /** 127 * Get a EAP-Method from a corresponding NAI realm that has one of them (EAP-SIM/AKA/AKA)'. 128 * 129 * @param realm a realm of the provider's credential. 130 * @param element The NAI Realm ANQP element 131 * @return a EAP Method (EAP-SIM/AKA/AKA') from matching NAI realm, {@code -1} otherwise. 132 */ getCarrierEapMethodFromMatchingNAIRealm(String realm, NAIRealmElement element)133 public static int getCarrierEapMethodFromMatchingNAIRealm(String realm, 134 NAIRealmElement element) { 135 if (element == null || element.getRealmDataList().isEmpty()) { 136 return -1; 137 } 138 139 for (NAIRealmData realmData : element.getRealmDataList()) { 140 int eapMethodID = getEapMethodForNAIRealmWithCarrier(realm, realmData); 141 if (eapMethodID != -1) { 142 return eapMethodID; 143 } 144 } 145 return -1; 146 } 147 148 /** 149 * Match the 3GPP Network in the ANQP element against the SIM credential of a provider. 150 * 151 * @param element 3GPP Network ANQP element 152 * @param imsiParam The IMSI parameter of the provider's SIM credential 153 * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's 154 * IMSI parameter 155 * @return true if a matched is found 156 */ matchThreeGPPNetwork(ThreeGPPNetworkElement element, IMSIParameter imsiParam, List<String> simImsiList)157 public static boolean matchThreeGPPNetwork(ThreeGPPNetworkElement element, 158 IMSIParameter imsiParam, List<String> simImsiList) { 159 if (element == null) { 160 return false; 161 } 162 for (CellularNetwork network : element.getNetworks()) { 163 if (matchCellularNetwork(network, imsiParam, simImsiList)) { 164 return true; 165 } 166 } 167 return false; 168 } 169 170 /** 171 * Match the given NAI Realm data against the realm and authentication method of a provider. 172 * 173 * @param realmData The NAI Realm data 174 * @param realm The realm of the provider's credential 175 * @param eapMethodID The EAP Method ID of the provider's credential 176 * @param authParam The authentication parameter of the provider's credential 177 * @return an integer indicating the match status 178 */ matchNAIRealmData(NAIRealmData realmData, String realm, int eapMethodID, AuthParam authParam)179 private static int matchNAIRealmData(NAIRealmData realmData, String realm, int eapMethodID, 180 AuthParam authParam) { 181 // Check for realm domain name match. 182 int realmMatch = AuthMatch.NONE; 183 for (String realmStr : realmData.getRealms()) { 184 if (DomainMatcher.arg2SubdomainOfArg1(realm, realmStr)) { 185 realmMatch = AuthMatch.REALM; 186 break; 187 } 188 } 189 190 if (realmData.getEAPMethods().isEmpty()) { 191 return realmMatch; 192 } 193 194 // Check for EAP method match. 195 int eapMethodMatch = AuthMatch.NONE; 196 for (EAPMethod eapMethod : realmData.getEAPMethods()) { 197 eapMethodMatch = matchEAPMethod(eapMethod, eapMethodID, authParam); 198 if (eapMethodMatch != AuthMatch.NONE) { 199 break; 200 } 201 } 202 203 if (eapMethodMatch == AuthMatch.NONE) { 204 return AuthMatch.NONE; 205 } 206 207 if (realmMatch == AuthMatch.NONE) { 208 return eapMethodMatch; 209 } 210 return realmMatch | eapMethodMatch; 211 } 212 getEapMethodForNAIRealmWithCarrier(String realm, NAIRealmData realmData)213 private static int getEapMethodForNAIRealmWithCarrier(String realm, 214 NAIRealmData realmData) { 215 int realmMatch = AuthMatch.NONE; 216 217 for (String realmStr : realmData.getRealms()) { 218 if (DomainMatcher.arg2SubdomainOfArg1(realm, realmStr)) { 219 realmMatch = AuthMatch.REALM; 220 break; 221 } 222 } 223 224 if (realmMatch == AuthMatch.NONE) { 225 return -1; 226 } 227 228 for (EAPMethod eapMethod : realmData.getEAPMethods()) { 229 if (isCarrierEapMethod(eapMethod.getEAPMethodID())) { 230 return eapMethod.getEAPMethodID(); 231 } 232 } 233 return -1; 234 } 235 236 /** 237 * Match the given EAPMethod against the authentication method of a provider. 238 * 239 * @param method The EAP Method 240 * @param eapMethodID The EAP Method ID of the provider's credential 241 * @param authParam The authentication parameter of the provider's credential 242 * @return an integer indicating the match status 243 */ matchEAPMethod(EAPMethod method, int eapMethodID, AuthParam authParam)244 private static int matchEAPMethod(EAPMethod method, int eapMethodID, AuthParam authParam) { 245 if (method.getEAPMethodID() != eapMethodID) { 246 return AuthMatch.NONE; 247 } 248 // Check for authentication parameter match. 249 if (authParam != null) { 250 Map<Integer, Set<AuthParam>> authParams = method.getAuthParams(); 251 if (authParams.isEmpty()) { 252 // no auth methods to match 253 return AuthMatch.METHOD; 254 } 255 Set<AuthParam> paramSet = authParams.get(authParam.getAuthTypeID()); 256 if (paramSet == null || !paramSet.contains(authParam)) { 257 return AuthMatch.NONE; 258 } 259 return AuthMatch.METHOD_PARAM; 260 } 261 return AuthMatch.METHOD; 262 } 263 264 /** 265 * Match a cellular network information in the 3GPP Network ANQP element against the SIM 266 * credential of a provider. 267 * 268 * @param network The cellular network that contained list of PLMNs 269 * @param imsiParam IMSI parameter of the provider 270 * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's 271 * IMSI parameter 272 * @return true if a match is found 273 */ matchCellularNetwork(CellularNetwork network, IMSIParameter imsiParam, List<String> simImsiList)274 private static boolean matchCellularNetwork(CellularNetwork network, IMSIParameter imsiParam, 275 List<String> simImsiList) { 276 for (String plmn : network.getPlmns()) { 277 if (matchMccMnc(plmn, imsiParam, simImsiList)) { 278 return true; 279 } 280 } 281 return false; 282 } 283 284 /** 285 * Match a MCC-MNC against the SIM credential of a provider. 286 * 287 * @param mccMnc The string containing MCC-MNC 288 * @param imsiParam The IMSI parameter of the provider 289 * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's 290 * IMSI parameter 291 * @return true if a match is found 292 */ matchMccMnc(String mccMnc, IMSIParameter imsiParam, List<String> simImsiList)293 private static boolean matchMccMnc(String mccMnc, IMSIParameter imsiParam, 294 List<String> simImsiList) { 295 if (imsiParam == null || simImsiList == null) { 296 return false; 297 } 298 // Match against the IMSI parameter in the provider. 299 if (!imsiParam.matchesMccMnc(mccMnc)) { 300 return false; 301 } 302 // Additional check for verifying the match with IMSIs from the SIM cards, since the IMSI 303 // parameter might not contain the full 6-digit MCC MNC (e.g. IMSI parameter is an IMSI 304 // prefix that contained less than 6-digit of numbers "12345*"). 305 for (String imsi : simImsiList) { 306 if (imsi.startsWith(mccMnc)) { 307 return true; 308 } 309 } 310 return false; 311 } 312 } 313