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