1 /* 2 * Copyright (C) 2017 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; 18 19 import static com.android.server.wifi.WifiConfigStore.ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION; 20 21 import android.annotation.Nullable; 22 import android.content.Context; 23 import android.net.IpConfiguration; 24 import android.net.wifi.WifiConfiguration; 25 import android.net.wifi.WifiConfiguration.NetworkSelectionStatus; 26 import android.net.wifi.WifiEnterpriseConfig; 27 import android.os.Process; 28 import android.util.Log; 29 import android.util.Pair; 30 31 import com.android.server.wifi.util.WifiConfigStoreEncryptionUtil; 32 import com.android.server.wifi.util.XmlUtil; 33 import com.android.server.wifi.util.XmlUtil.IpConfigurationXmlUtil; 34 import com.android.server.wifi.util.XmlUtil.NetworkSelectionStatusXmlUtil; 35 import com.android.server.wifi.util.XmlUtil.WifiConfigurationXmlUtil; 36 import com.android.server.wifi.util.XmlUtil.WifiEnterpriseConfigXmlUtil; 37 38 import org.xmlpull.v1.XmlPullParser; 39 import org.xmlpull.v1.XmlPullParserException; 40 import org.xmlpull.v1.XmlSerializer; 41 42 import java.io.IOException; 43 import java.util.ArrayList; 44 import java.util.Collections; 45 import java.util.Comparator; 46 import java.util.List; 47 48 /** 49 * This class performs serialization and parsing of XML data block that contain the list of WiFi 50 * network configurations (XML block data inside <NetworkList> tag). 51 */ 52 public abstract class NetworkListStoreData implements WifiConfigStore.StoreData { 53 private static final String TAG = "NetworkListStoreData"; 54 55 private static final String XML_TAG_SECTION_HEADER_NETWORK_LIST = "NetworkList"; 56 private static final String XML_TAG_SECTION_HEADER_NETWORK = "Network"; 57 private static final String XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION = "WifiConfiguration"; 58 private static final String XML_TAG_SECTION_HEADER_NETWORK_STATUS = "NetworkStatus"; 59 private static final String XML_TAG_SECTION_HEADER_IP_CONFIGURATION = "IpConfiguration"; 60 private static final String XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION = 61 "WifiEnterpriseConfiguration"; 62 63 private final Context mContext; 64 65 /** 66 * List of saved shared networks visible to all the users to be stored in the store file. 67 */ 68 private List<WifiConfiguration> mConfigurations; 69 NetworkListStoreData(Context context)70 NetworkListStoreData(Context context) { 71 mContext = context; 72 } 73 74 @Override serializeData(XmlSerializer out, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)75 public void serializeData(XmlSerializer out, 76 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) 77 throws XmlPullParserException, IOException { 78 serializeNetworkList(out, mConfigurations, encryptionUtil); 79 } 80 81 @Override deserializeData(XmlPullParser in, int outerTagDepth, @WifiConfigStore.Version int version, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)82 public void deserializeData(XmlPullParser in, int outerTagDepth, 83 @WifiConfigStore.Version int version, 84 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) 85 throws XmlPullParserException, IOException { 86 // Ignore empty reads. 87 if (in == null) { 88 return; 89 } 90 mConfigurations = parseNetworkList(in, outerTagDepth, version, encryptionUtil); 91 } 92 93 @Override resetData()94 public void resetData() { 95 mConfigurations = null; 96 } 97 98 @Override hasNewDataToSerialize()99 public boolean hasNewDataToSerialize() { 100 // always persist. 101 return true; 102 } 103 104 @Override getName()105 public String getName() { 106 return XML_TAG_SECTION_HEADER_NETWORK_LIST; 107 } 108 setConfigurations(List<WifiConfiguration> configs)109 public void setConfigurations(List<WifiConfiguration> configs) { 110 mConfigurations = configs; 111 } 112 113 /** 114 * An empty list will be returned if no shared configurations. 115 * 116 * @return List of {@link WifiConfiguration} 117 */ getConfigurations()118 public List<WifiConfiguration> getConfigurations() { 119 if (mConfigurations == null) { 120 return new ArrayList<WifiConfiguration>(); 121 } 122 return mConfigurations; 123 } 124 125 /** 126 * Serialize the list of {@link WifiConfiguration} to an output stream in XML format. 127 * 128 * @param out The output stream to serialize the data to 129 * @param networkList The network list to serialize 130 * @param encryptionUtil Instance of {@link WifiConfigStoreEncryptionUtil} 131 * @throws XmlPullParserException 132 * @throws IOException 133 */ serializeNetworkList(XmlSerializer out, List<WifiConfiguration> networkList, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)134 private void serializeNetworkList(XmlSerializer out, List<WifiConfiguration> networkList, 135 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) 136 throws XmlPullParserException, IOException { 137 if (networkList == null) { 138 return; 139 } 140 // Sort by SSID 141 Collections.sort(networkList, Comparator.comparing(a -> a.SSID)); 142 for (WifiConfiguration network : networkList) { 143 serializeNetwork(out, network, encryptionUtil); 144 } 145 } 146 147 /** 148 * Serialize a {@link WifiConfiguration} to an output stream in XML format. 149 * 150 * @param out The output stream to serialize the data to 151 * @param config The network config to serialize 152 * @param encryptionUtil Instance of {@link WifiConfigStoreEncryptionUtil} 153 * @throws XmlPullParserException 154 * @throws IOException 155 */ serializeNetwork(XmlSerializer out, WifiConfiguration config, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)156 private void serializeNetwork(XmlSerializer out, WifiConfiguration config, 157 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) 158 throws XmlPullParserException, IOException { 159 XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK); 160 161 // Serialize WifiConfiguration. 162 XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION); 163 WifiConfigurationXmlUtil.writeToXmlForConfigStore(out, config, encryptionUtil); 164 XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION); 165 166 // Serialize network selection status. 167 XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS); 168 NetworkSelectionStatusXmlUtil.writeToXml(out, config.getNetworkSelectionStatus()); 169 XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK_STATUS); 170 171 // Serialize IP configuration. 172 XmlUtil.writeNextSectionStart(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION); 173 IpConfigurationXmlUtil.writeToXml(out, config.getIpConfiguration()); 174 XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_IP_CONFIGURATION); 175 176 // Serialize enterprise configuration for enterprise networks. 177 if (config.enterpriseConfig != null 178 && config.enterpriseConfig.getEapMethod() != WifiEnterpriseConfig.Eap.NONE) { 179 XmlUtil.writeNextSectionStart( 180 out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION); 181 WifiEnterpriseConfigXmlUtil.writeToXml(out, config.enterpriseConfig, encryptionUtil); 182 XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION); 183 } 184 185 XmlUtil.writeNextSectionEnd(out, XML_TAG_SECTION_HEADER_NETWORK); 186 } 187 188 /** 189 * Parse a list of {@link WifiConfiguration} from an input stream in XML format. 190 * 191 * @param in The input stream to read from 192 * @param outerTagDepth The XML tag depth of the outer XML block 193 * @param version Version of config store file. 194 * @param encryptionUtil Instance of {@link WifiConfigStoreEncryptionUtil} 195 * @return List of {@link WifiConfiguration} 196 * @throws XmlPullParserException 197 * @throws IOException 198 */ parseNetworkList(XmlPullParser in, int outerTagDepth, @WifiConfigStore.Version int version, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)199 private List<WifiConfiguration> parseNetworkList(XmlPullParser in, int outerTagDepth, 200 @WifiConfigStore.Version int version, 201 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) 202 throws XmlPullParserException, IOException { 203 List<WifiConfiguration> networkList = new ArrayList<>(); 204 while (XmlUtil.gotoNextSectionWithNameOrEnd(in, XML_TAG_SECTION_HEADER_NETWORK, 205 outerTagDepth)) { 206 // Try/catch only runtime exceptions (like illegal args), any XML/IO exceptions are 207 // fatal and should abort the entire loading process. 208 try { 209 WifiConfiguration config = 210 parseNetwork(in, outerTagDepth + 1, version, encryptionUtil); 211 networkList.add(config); 212 } catch (RuntimeException e) { 213 // Failed to parse this network, skip it. 214 Log.e(TAG, "Failed to parse network config. Skipping...", e); 215 } 216 } 217 return networkList; 218 } 219 220 /** 221 * Parse a {@link WifiConfiguration} from an input stream in XML format. 222 * 223 * @param in The input stream to read from 224 * @param outerTagDepth The XML tag depth of the outer XML block 225 * @param version Version of config store file. 226 * @param encryptionUtil Instance of {@link WifiConfigStoreEncryptionUtil} 227 * @return {@link WifiConfiguration} 228 * @throws XmlPullParserException 229 * @throws IOException 230 */ parseNetwork(XmlPullParser in, int outerTagDepth, @WifiConfigStore.Version int version, @Nullable WifiConfigStoreEncryptionUtil encryptionUtil)231 private WifiConfiguration parseNetwork(XmlPullParser in, int outerTagDepth, 232 @WifiConfigStore.Version int version, 233 @Nullable WifiConfigStoreEncryptionUtil encryptionUtil) 234 throws XmlPullParserException, IOException { 235 Pair<String, WifiConfiguration> parsedConfig = null; 236 NetworkSelectionStatus status = null; 237 IpConfiguration ipConfiguration = null; 238 WifiEnterpriseConfig enterpriseConfig = null; 239 240 String[] headerName = new String[1]; 241 while (XmlUtil.gotoNextSectionOrEnd(in, headerName, outerTagDepth)) { 242 switch (headerName[0]) { 243 case XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION: 244 if (parsedConfig != null) { 245 throw new XmlPullParserException("Detected duplicate tag for: " 246 + XML_TAG_SECTION_HEADER_WIFI_CONFIGURATION); 247 } 248 parsedConfig = WifiConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1, 249 version >= ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION, 250 encryptionUtil, false); 251 break; 252 case XML_TAG_SECTION_HEADER_NETWORK_STATUS: 253 if (status != null) { 254 throw new XmlPullParserException("Detected duplicate tag for: " 255 + XML_TAG_SECTION_HEADER_NETWORK_STATUS); 256 } 257 status = NetworkSelectionStatusXmlUtil.parseFromXml(in, outerTagDepth + 1); 258 break; 259 case XML_TAG_SECTION_HEADER_IP_CONFIGURATION: 260 if (ipConfiguration != null) { 261 throw new XmlPullParserException("Detected duplicate tag for: " 262 + XML_TAG_SECTION_HEADER_IP_CONFIGURATION); 263 } 264 ipConfiguration = IpConfigurationXmlUtil.parseFromXml(in, outerTagDepth + 1); 265 break; 266 case XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION: 267 if (enterpriseConfig != null) { 268 throw new XmlPullParserException("Detected duplicate tag for: " 269 + XML_TAG_SECTION_HEADER_WIFI_ENTERPRISE_CONFIGURATION); 270 } 271 enterpriseConfig = 272 WifiEnterpriseConfigXmlUtil.parseFromXml(in, outerTagDepth + 1, 273 version >= ENCRYPT_CREDENTIALS_CONFIG_STORE_DATA_VERSION, 274 encryptionUtil); 275 break; 276 default: 277 Log.w(TAG, "Ignoring unknown tag under " + XML_TAG_SECTION_HEADER_NETWORK 278 + ": " + headerName[0]); 279 break; 280 } 281 } 282 if (parsedConfig == null || parsedConfig.first == null || parsedConfig.second == null) { 283 throw new XmlPullParserException("XML parsing of wifi configuration failed"); 284 } 285 String configKeyParsed = parsedConfig.first; 286 WifiConfiguration configuration = parsedConfig.second; 287 288 configuration.convertLegacyFieldsToSecurityParamsIfNeeded(); 289 290 // b/153435438: Added to deal with badly formed WifiConfiguration from apps. 291 if (configuration.preSharedKey != null && !configuration.needsPreSharedKey()) { 292 Log.e(TAG, "preSharedKey set with an invalid KeyMgmt, resetting KeyMgmt to WPA_PSK"); 293 configuration.setSecurityParams(WifiConfiguration.SECURITY_TYPE_PSK); 294 // Recreate configKey to pass the check below. 295 configKeyParsed = configuration.getKey(); 296 } 297 298 String configKeyCalculated = configuration.getKey(); 299 if (!configKeyParsed.equals(configKeyCalculated)) { 300 throw new IllegalStateException( 301 "Configuration key does not match. Retrieved: " + configKeyParsed 302 + ", Calculated: " + configKeyCalculated); 303 } 304 // Set creatorUid/creatorName for networks which don't have it set to valid value. 305 String creatorName = mContext.getPackageManager().getNameForUid(configuration.creatorUid); 306 if (creatorName == null) { 307 Log.e(TAG, "Invalid creatorUid for saved network " + configuration.getKey() 308 + ", creatorUid=" + configuration.creatorUid); 309 configuration.creatorUid = Process.SYSTEM_UID; 310 configuration.creatorName = 311 mContext.getPackageManager().getNameForUid(Process.SYSTEM_UID); 312 } else if (!creatorName.equals(configuration.creatorName)) { 313 Log.w(TAG, "Invalid creatorName for saved network " + configuration.getKey() 314 + ", creatorUid=" + configuration.creatorUid 315 + ", creatorName=" + configuration.creatorName); 316 configuration.creatorName = creatorName; 317 } 318 319 configuration.setNetworkSelectionStatus(status); 320 configuration.setIpConfiguration(ipConfiguration); 321 if (enterpriseConfig != null) { 322 configuration.enterpriseConfig = enterpriseConfig; 323 } 324 return configuration; 325 } 326 } 327 328