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