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.os.Parcel;
20 import android.os.Parcelable;
21 import android.text.TextUtils;
22 import android.util.Log;
23 
24 import java.net.InetAddress;
25 import java.nio.charset.StandardCharsets;
26 
27 /**
28  * Parcel-like entity class for VPN profiles. To keep things simple, all
29  * fields are package private. Methods are provided for serialization, so
30  * storage can be implemented easily. Two rules are set for this class.
31  * First, all fields must be kept non-null. Second, always make a copy
32  * using clone() before modifying.
33  *
34  * @hide
35  */
36 public class VpnProfile implements Cloneable, Parcelable {
37     private static final String TAG = "VpnProfile";
38 
39     // Match these constants with R.array.vpn_types.
40     public static final int TYPE_PPTP = 0;
41     public static final int TYPE_L2TP_IPSEC_PSK = 1;
42     public static final int TYPE_L2TP_IPSEC_RSA = 2;
43     public static final int TYPE_IPSEC_XAUTH_PSK = 3;
44     public static final int TYPE_IPSEC_XAUTH_RSA = 4;
45     public static final int TYPE_IPSEC_HYBRID_RSA = 5;
46     public static final int TYPE_MAX = 5;
47 
48     // Entity fields.
49     public final String key;           // -1
50     public String name = "";           // 0
51     public int type = TYPE_PPTP;       // 1
52     public String server = "";         // 2
53     public String username = "";       // 3
54     public String password = "";       // 4
55     public String dnsServers = "";     // 5
56     public String searchDomains = "";  // 6
57     public String routes = "";         // 7
58     public boolean mppe = true;        // 8
59     public String l2tpSecret = "";     // 9
60     public String ipsecIdentifier = "";// 10
61     public String ipsecSecret = "";    // 11
62     public String ipsecUserCert = "";  // 12
63     public String ipsecCaCert = "";    // 13
64     public String ipsecServerCert = "";// 14
65 
66     // Helper fields.
67     public boolean saveLogin = false;
68 
VpnProfile(String key)69     public VpnProfile(String key) {
70         this.key = key;
71     }
72 
VpnProfile(Parcel in)73     public VpnProfile(Parcel in) {
74         key = in.readString();
75         name = in.readString();
76         type = in.readInt();
77         server = in.readString();
78         username = in.readString();
79         password = in.readString();
80         dnsServers = in.readString();
81         searchDomains = in.readString();
82         routes = in.readString();
83         mppe = in.readInt() != 0;
84         l2tpSecret = in.readString();
85         ipsecIdentifier = in.readString();
86         ipsecSecret = in.readString();
87         ipsecUserCert = in.readString();
88         ipsecCaCert = in.readString();
89         ipsecServerCert = in.readString();
90         saveLogin = in.readInt() != 0;
91     }
92 
93     @Override
writeToParcel(Parcel out, int flags)94     public void writeToParcel(Parcel out, int flags) {
95         out.writeString(key);
96         out.writeString(name);
97         out.writeInt(type);
98         out.writeString(server);
99         out.writeString(username);
100         out.writeString(password);
101         out.writeString(dnsServers);
102         out.writeString(searchDomains);
103         out.writeString(routes);
104         out.writeInt(mppe ? 1 : 0);
105         out.writeString(l2tpSecret);
106         out.writeString(ipsecIdentifier);
107         out.writeString(ipsecSecret);
108         out.writeString(ipsecUserCert);
109         out.writeString(ipsecCaCert);
110         out.writeString(ipsecServerCert);
111         out.writeInt(saveLogin ? 1 : 0);
112     }
113 
decode(String key, byte[] value)114     public static VpnProfile decode(String key, byte[] value) {
115         try {
116             if (key == null) {
117                 return null;
118             }
119 
120             String[] values = new String(value, StandardCharsets.UTF_8).split("\0", -1);
121             // There can be 14 or 15 values in ICS MR1.
122             if (values.length < 14 || values.length > 15) {
123                 return null;
124             }
125 
126             VpnProfile profile = new VpnProfile(key);
127             profile.name = values[0];
128             profile.type = Integer.valueOf(values[1]);
129             if (profile.type < 0 || profile.type > TYPE_MAX) {
130                 return null;
131             }
132             profile.server = values[2];
133             profile.username = values[3];
134             profile.password = values[4];
135             profile.dnsServers = values[5];
136             profile.searchDomains = values[6];
137             profile.routes = values[7];
138             profile.mppe = Boolean.valueOf(values[8]);
139             profile.l2tpSecret = values[9];
140             profile.ipsecIdentifier = values[10];
141             profile.ipsecSecret = values[11];
142             profile.ipsecUserCert = values[12];
143             profile.ipsecCaCert = values[13];
144             profile.ipsecServerCert = (values.length > 14) ? values[14] : "";
145 
146             profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
147             return profile;
148         } catch (Exception e) {
149             // ignore
150         }
151         return null;
152     }
153 
encode()154     public byte[] encode() {
155         StringBuilder builder = new StringBuilder(name);
156         builder.append('\0').append(type);
157         builder.append('\0').append(server);
158         builder.append('\0').append(saveLogin ? username : "");
159         builder.append('\0').append(saveLogin ? password : "");
160         builder.append('\0').append(dnsServers);
161         builder.append('\0').append(searchDomains);
162         builder.append('\0').append(routes);
163         builder.append('\0').append(mppe);
164         builder.append('\0').append(l2tpSecret);
165         builder.append('\0').append(ipsecIdentifier);
166         builder.append('\0').append(ipsecSecret);
167         builder.append('\0').append(ipsecUserCert);
168         builder.append('\0').append(ipsecCaCert);
169         builder.append('\0').append(ipsecServerCert);
170         return builder.toString().getBytes(StandardCharsets.UTF_8);
171     }
172 
173     /**
174      * Test if profile is valid for lockdown, which requires IPv4 address for
175      * both server and DNS. Server hostnames would require using DNS before
176      * connection.
177      */
isValidLockdownProfile()178     public boolean isValidLockdownProfile() {
179         try {
180             InetAddress.parseNumericAddress(server);
181 
182             for (String dnsServer : dnsServers.split(" +")) {
183                 InetAddress.parseNumericAddress(this.dnsServers);
184             }
185             if (TextUtils.isEmpty(dnsServers)) {
186                 Log.w(TAG, "DNS required");
187                 return false;
188             }
189 
190             // Everything checked out above
191             return true;
192 
193         } catch (IllegalArgumentException e) {
194             Log.w(TAG, "Invalid address", e);
195             return false;
196         }
197     }
198 
199     public static final Creator<VpnProfile> CREATOR = new Creator<VpnProfile>() {
200         @Override
201         public VpnProfile createFromParcel(Parcel in) {
202             return new VpnProfile(in);
203         }
204 
205         @Override
206         public VpnProfile[] newArray(int size) {
207             return new VpnProfile[size];
208         }
209     };
210 
211     @Override
describeContents()212     public int describeContents() {
213         return 0;
214     }
215 }
216