1 /*
2  * Copyright (C) 2011 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.internal.net;
18 
19 import android.annotation.UnsupportedAppUsage;
20 import android.os.Build;
21 import android.net.ProxyInfo;
22 import android.net.Uri;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.text.TextUtils;
26 
27 import java.net.InetAddress;
28 import java.nio.charset.StandardCharsets;
29 
30 /**
31  * Parcel-like entity class for VPN profiles. To keep things simple, all
32  * fields are package private. Methods are provided for serialization, so
33  * storage can be implemented easily. Two rules are set for this class.
34  * First, all fields must be kept non-null. Second, always make a copy
35  * using clone() before modifying.
36  *
37  * @hide
38  */
39 public class VpnProfile implements Cloneable, Parcelable {
40     private static final String TAG = "VpnProfile";
41 
42     // Match these constants with R.array.vpn_types.
43     public static final int TYPE_PPTP = 0;
44     public static final int TYPE_L2TP_IPSEC_PSK = 1;
45     public static final int TYPE_L2TP_IPSEC_RSA = 2;
46     public static final int TYPE_IPSEC_XAUTH_PSK = 3;
47     public static final int TYPE_IPSEC_XAUTH_RSA = 4;
48     public static final int TYPE_IPSEC_HYBRID_RSA = 5;
49     public static final int TYPE_MAX = 5;
50 
51     // Match these constants with R.array.vpn_proxy_settings.
52     public static final int PROXY_NONE = 0;
53     public static final int PROXY_MANUAL = 1;
54 
55     // Entity fields.
56     @UnsupportedAppUsage
57     public final String key;           // -1
58     @UnsupportedAppUsage
59     public String name = "";           // 0
60     @UnsupportedAppUsage
61     public int type = TYPE_PPTP;       // 1
62     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
63     public String server = "";         // 2
64     @UnsupportedAppUsage
65     public String username = "";       // 3
66     public String password = "";       // 4
67     public String dnsServers = "";     // 5
68     public String searchDomains = "";  // 6
69     public String routes = "";         // 7
70     public boolean mppe = true;        // 8
71     public String l2tpSecret = "";     // 9
72     public String ipsecIdentifier = "";// 10
73     public String ipsecSecret = "";    // 11
74     public String ipsecUserCert = "";  // 12
75     public String ipsecCaCert = "";    // 13
76     public String ipsecServerCert = "";// 14
77     public ProxyInfo proxy = null;     // 15~18
78 
79     // Helper fields.
80     @UnsupportedAppUsage
81     public boolean saveLogin = false;
82 
VpnProfile(String key)83     public VpnProfile(String key) {
84         this.key = key;
85     }
86 
87     @UnsupportedAppUsage
VpnProfile(Parcel in)88     public VpnProfile(Parcel in) {
89         key = in.readString();
90         name = in.readString();
91         type = in.readInt();
92         server = in.readString();
93         username = in.readString();
94         password = in.readString();
95         dnsServers = in.readString();
96         searchDomains = in.readString();
97         routes = in.readString();
98         mppe = in.readInt() != 0;
99         l2tpSecret = in.readString();
100         ipsecIdentifier = in.readString();
101         ipsecSecret = in.readString();
102         ipsecUserCert = in.readString();
103         ipsecCaCert = in.readString();
104         ipsecServerCert = in.readString();
105         saveLogin = in.readInt() != 0;
106         proxy = in.readParcelable(null);
107     }
108 
109     @Override
writeToParcel(Parcel out, int flags)110     public void writeToParcel(Parcel out, int flags) {
111         out.writeString(key);
112         out.writeString(name);
113         out.writeInt(type);
114         out.writeString(server);
115         out.writeString(username);
116         out.writeString(password);
117         out.writeString(dnsServers);
118         out.writeString(searchDomains);
119         out.writeString(routes);
120         out.writeInt(mppe ? 1 : 0);
121         out.writeString(l2tpSecret);
122         out.writeString(ipsecIdentifier);
123         out.writeString(ipsecSecret);
124         out.writeString(ipsecUserCert);
125         out.writeString(ipsecCaCert);
126         out.writeString(ipsecServerCert);
127         out.writeInt(saveLogin ? 1 : 0);
128         out.writeParcelable(proxy, flags);
129     }
130 
131     @UnsupportedAppUsage
decode(String key, byte[] value)132     public static VpnProfile decode(String key, byte[] value) {
133         try {
134             if (key == null) {
135                 return null;
136             }
137 
138             String[] values = new String(value, StandardCharsets.UTF_8).split("\0", -1);
139             // There can be 14 - 19 Bytes in values.length.
140             if (values.length < 14 || values.length > 19) {
141                 return null;
142             }
143 
144             VpnProfile profile = new VpnProfile(key);
145             profile.name = values[0];
146             profile.type = Integer.parseInt(values[1]);
147             if (profile.type < 0 || profile.type > TYPE_MAX) {
148                 return null;
149             }
150             profile.server = values[2];
151             profile.username = values[3];
152             profile.password = values[4];
153             profile.dnsServers = values[5];
154             profile.searchDomains = values[6];
155             profile.routes = values[7];
156             profile.mppe = Boolean.parseBoolean(values[8]);
157             profile.l2tpSecret = values[9];
158             profile.ipsecIdentifier = values[10];
159             profile.ipsecSecret = values[11];
160             profile.ipsecUserCert = values[12];
161             profile.ipsecCaCert = values[13];
162             profile.ipsecServerCert = (values.length > 14) ? values[14] : "";
163             if (values.length > 15) {
164                 String host = (values.length > 15) ? values[15] : "";
165                 String port = (values.length > 16) ? values[16] : "";
166                 String exclList = (values.length > 17) ? values[17] : "";
167                 String pacFileUrl = (values.length > 18) ? values[18] : "";
168                 if (pacFileUrl.isEmpty()) {
169                     profile.proxy = new ProxyInfo(host, port.isEmpty() ?
170                             0 : Integer.parseInt(port), exclList);
171                 } else {
172                     profile.proxy = new ProxyInfo(pacFileUrl);
173                 }
174             } // else profle.proxy = null
175             profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
176             return profile;
177         } catch (Exception e) {
178             // ignore
179         }
180         return null;
181     }
182 
encode()183     public byte[] encode() {
184         StringBuilder builder = new StringBuilder(name);
185         builder.append('\0').append(type);
186         builder.append('\0').append(server);
187         builder.append('\0').append(saveLogin ? username : "");
188         builder.append('\0').append(saveLogin ? password : "");
189         builder.append('\0').append(dnsServers);
190         builder.append('\0').append(searchDomains);
191         builder.append('\0').append(routes);
192         builder.append('\0').append(mppe);
193         builder.append('\0').append(l2tpSecret);
194         builder.append('\0').append(ipsecIdentifier);
195         builder.append('\0').append(ipsecSecret);
196         builder.append('\0').append(ipsecUserCert);
197         builder.append('\0').append(ipsecCaCert);
198         builder.append('\0').append(ipsecServerCert);
199         if (proxy != null) {
200             builder.append('\0').append(proxy.getHost() != null ? proxy.getHost() : "");
201             builder.append('\0').append(proxy.getPort());
202             builder.append('\0').append(proxy.getExclusionListAsString() != null ?
203                     proxy.getExclusionListAsString() : "");
204             builder.append('\0').append(proxy.getPacFileUrl().toString());
205         }
206         return builder.toString().getBytes(StandardCharsets.UTF_8);
207     }
208 
209     /**
210      * Tests if profile is valid for lockdown, which requires IPv4 address for
211      * both server and DNS. Server hostnames would require using DNS before
212      * connection.
213      */
isValidLockdownProfile()214     public boolean isValidLockdownProfile() {
215         return isTypeValidForLockdown()
216                 && isServerAddressNumeric()
217                 && hasDns()
218                 && areDnsAddressesNumeric();
219     }
220 
221     /** Returns {@code true} if the VPN type is valid for lockdown. */
isTypeValidForLockdown()222     public boolean isTypeValidForLockdown() {
223         // b/7064069: lockdown firewall blocks ports used for PPTP
224         return type != TYPE_PPTP;
225     }
226 
227     /** Returns {@code true} if the server address is numeric, e.g. 8.8.8.8 */
isServerAddressNumeric()228     public boolean isServerAddressNumeric() {
229         try {
230             InetAddress.parseNumericAddress(server);
231         } catch (IllegalArgumentException e) {
232             return false;
233         }
234         return true;
235     }
236 
237     /** Returns {@code true} if one or more DNS servers are specified. */
hasDns()238     public boolean hasDns() {
239         return !TextUtils.isEmpty(dnsServers);
240     }
241 
242     /**
243      * Returns {@code true} if all DNS servers have numeric addresses,
244      * e.g. 8.8.8.8
245      */
areDnsAddressesNumeric()246     public boolean areDnsAddressesNumeric() {
247         try {
248             for (String dnsServer : dnsServers.split(" +")) {
249                 InetAddress.parseNumericAddress(dnsServer);
250             }
251         } catch (IllegalArgumentException e) {
252             return false;
253         }
254         return true;
255     }
256 
257     public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
258         @Override
259         public VpnProfile createFromParcel(Parcel in) {
260             return new VpnProfile(in);
261         }
262 
263         @Override
264         public VpnProfile[] newArray(int size) {
265             return new VpnProfile[size];
266         }
267     };
268 
269     @Override
describeContents()270     public int describeContents() {
271         return 0;
272     }
273 }
274