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