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 17package com.android.server.wifi.hotspot2; 18 19import com.android.server.wifi.IMSIParameter; 20import com.android.server.wifi.hotspot2.anqp.CellularNetwork; 21import com.android.server.wifi.hotspot2.anqp.DomainNameElement; 22import com.android.server.wifi.hotspot2.anqp.NAIRealmData; 23import com.android.server.wifi.hotspot2.anqp.NAIRealmElement; 24import com.android.server.wifi.hotspot2.anqp.RoamingConsortiumElement; 25import com.android.server.wifi.hotspot2.anqp.ThreeGPPNetworkElement; 26import com.android.server.wifi.hotspot2.anqp.eap.AuthParam; 27import com.android.server.wifi.hotspot2.anqp.eap.EAPMethod; 28 29import java.util.List; 30import java.util.Map; 31import java.util.Set; 32 33/** 34 * Utility class for providing matching functions against ANQP elements. 35 */ 36public class ANQPMatcher { 37 /** 38 * Match the domain names in the ANQP element against the provider's FQDN and SIM credential. 39 * The Domain Name ANQP element might contain domains for 3GPP network (e.g. 40 * wlan.mnc*.mcc*.3gppnetwork.org), so we should match that against the provider's SIM 41 * credential if one is provided. 42 * 43 * @param element The Domain Name ANQP element 44 * @param fqdn The FQDN to compare against 45 * @param imsiParam The IMSI parameter of the provider 46 * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's 47 * IMSI parameter 48 * @return true if a match is found 49 */ 50 public static boolean matchDomainName(DomainNameElement element, String fqdn, 51 IMSIParameter imsiParam, List<String> simImsiList) { 52 if (element == null) { 53 return false; 54 } 55 56 for (String domain : element.getDomains()) { 57 if (DomainMatcher.arg2SubdomainOfArg1(fqdn, domain)) { 58 return true; 59 } 60 61 // Try to retrieve the MCC-MNC string from the domain (for 3GPP network domain) and 62 // match against the provider's SIM credential. 63 if (matchMccMnc(Utils.getMccMnc(Utils.splitDomain(domain)), imsiParam, simImsiList)) { 64 return true; 65 } 66 } 67 return false; 68 } 69 70 /** 71 * Match the roaming consortium OIs in the ANQP element against the roaming consortium OIs 72 * of a provider. 73 * 74 * @param element The Roaming Consortium ANQP element 75 * @param providerOIs The roaming consortium OIs of the provider 76 * @return true if a match is found 77 */ 78 public static boolean matchRoamingConsortium(RoamingConsortiumElement element, 79 long[] providerOIs) { 80 if (element == null) { 81 return false; 82 } 83 if (providerOIs == null) { 84 return false; 85 } 86 List<Long> rcOIs = element.getOIs(); 87 for (long oi : providerOIs) { 88 if (rcOIs.contains(oi)) { 89 return true; 90 } 91 } 92 return false; 93 } 94 95 /** 96 * Match the NAI realm in the ANQP element against the realm and authentication method of 97 * a provider. 98 * 99 * @param element The NAI Realm ANQP element 100 * @param realm The realm of the provider's credential 101 * @param eapMethodID The EAP Method ID of the provider's credential 102 * @param authParam The authentication parameter of the provider's credential 103 * @return an integer indicating the match status 104 */ 105 public static int matchNAIRealm(NAIRealmElement element, String realm, int eapMethodID, 106 AuthParam authParam) { 107 if (element == null || element.getRealmDataList().isEmpty()) { 108 return AuthMatch.INDETERMINATE; 109 } 110 111 int bestMatch = AuthMatch.NONE; 112 for (NAIRealmData realmData : element.getRealmDataList()) { 113 int match = matchNAIRealmData(realmData, realm, eapMethodID, authParam); 114 if (match > bestMatch) { 115 bestMatch = match; 116 if (bestMatch == AuthMatch.EXACT) { 117 break; 118 } 119 } 120 } 121 return bestMatch; 122 } 123 124 /** 125 * Match the 3GPP Network in the ANQP element against the SIM credential of a provider. 126 * 127 * @param element 3GPP Network ANQP element 128 * @param imsiParam The IMSI parameter of the provider's SIM credential 129 * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's 130 * IMSI parameter 131 * @return true if a matched is found 132 */ 133 public static boolean matchThreeGPPNetwork(ThreeGPPNetworkElement element, 134 IMSIParameter imsiParam, List<String> simImsiList) { 135 if (element == null) { 136 return false; 137 } 138 for (CellularNetwork network : element.getNetworks()) { 139 if (matchCellularNetwork(network, imsiParam, simImsiList)) { 140 return true; 141 } 142 } 143 return false; 144 } 145 146 /** 147 * Match the given NAI Realm data against the realm and authentication method of a provider. 148 * 149 * @param realmData The NAI Realm data 150 * @param realm The realm of the provider's credential 151 * @param eapMethodID The EAP Method ID of the provider's credential 152 * @param authParam The authentication parameter of the provider's credential 153 * @return an integer indicating the match status 154 */ 155 private static int matchNAIRealmData(NAIRealmData realmData, String realm, int eapMethodID, 156 AuthParam authParam) { 157 // Check for realm domain name match. 158 int realmMatch = AuthMatch.NONE; 159 for (String realmStr : realmData.getRealms()) { 160 if (DomainMatcher.arg2SubdomainOfArg1(realm, realmStr)) { 161 realmMatch = AuthMatch.REALM; 162 break; 163 } 164 } 165 166 if (realmData.getEAPMethods().isEmpty()) { 167 return realmMatch; 168 } 169 170 // Check for EAP method match. 171 int eapMethodMatch = AuthMatch.NONE; 172 for (EAPMethod eapMethod : realmData.getEAPMethods()) { 173 eapMethodMatch = matchEAPMethod(eapMethod, eapMethodID, authParam); 174 if (eapMethodMatch != AuthMatch.NONE) { 175 break; 176 } 177 } 178 179 if (eapMethodMatch == AuthMatch.NONE) { 180 return AuthMatch.NONE; 181 } 182 183 if (realmMatch == AuthMatch.NONE) { 184 return eapMethodMatch; 185 } 186 return realmMatch | eapMethodMatch; 187 } 188 189 /** 190 * Match the given EAPMethod against the authentication method of a provider. 191 * 192 * @param method The EAP Method 193 * @param eapMethodID The EAP Method ID of the provider's credential 194 * @param authParam The authentication parameter of the provider's credential 195 * @return an integer indicating the match status 196 */ 197 private static int matchEAPMethod(EAPMethod method, int eapMethodID, AuthParam authParam) { 198 if (method.getEAPMethodID() != eapMethodID) { 199 return AuthMatch.NONE; 200 } 201 // Check for authentication parameter match. 202 if (authParam != null) { 203 Map<Integer, Set<AuthParam>> authParams = method.getAuthParams(); 204 Set<AuthParam> paramSet = authParams.get(authParam.getAuthTypeID()); 205 if (paramSet == null || !paramSet.contains(authParam)) { 206 return AuthMatch.NONE; 207 } 208 return AuthMatch.METHOD_PARAM; 209 } 210 return AuthMatch.METHOD; 211 } 212 213 /** 214 * Match a cellular network information in the 3GPP Network ANQP element against the SIM 215 * credential of a provider. 216 * 217 * @param network The cellular network that contained list of PLMNs 218 * @param imsiParam IMSI parameter of the provider 219 * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's 220 * IMSI parameter 221 * @return true if a match is found 222 */ 223 private static boolean matchCellularNetwork(CellularNetwork network, IMSIParameter imsiParam, 224 List<String> simImsiList) { 225 for (String plmn : network.getPlmns()) { 226 if (matchMccMnc(plmn, imsiParam, simImsiList)) { 227 return true; 228 } 229 } 230 return false; 231 } 232 233 /** 234 * Match a MCC-MNC against the SIM credential of a provider. 235 * 236 * @param mccMnc The string containing MCC-MNC 237 * @param imsiParam The IMSI parameter of the provider 238 * @param simImsiList The list of IMSI from the installed SIM cards that matched provider's 239 * IMSI parameter 240 * @return true if a match is found 241 */ 242 private static boolean matchMccMnc(String mccMnc, IMSIParameter imsiParam, 243 List<String> simImsiList) { 244 if (imsiParam == null || simImsiList == null) { 245 return false; 246 } 247 // Match against the IMSI parameter in the provider. 248 if (!imsiParam.matchesMccMnc(mccMnc)) { 249 return false; 250 } 251 // Additional check for verifying the match with IMSIs from the SIM cards, since the IMSI 252 // parameter might not contain the full 6-digit MCC MNC (e.g. IMSI parameter is an IMSI 253 // prefix that contained less than 6-digit of numbers "12345*"). 254 for (String imsi : simImsiList) { 255 if (imsi.startsWith(mccMnc)) { 256 return true; 257 } 258 } 259 return false; 260 } 261} 262