vpn: Add initial support for async connect
[platform/upstream/connman.git] / vpn / plugins / openconnect.c
1 /*
2  *
3  *  ConnMan VPN daemon
4  *
5  *  Copyright (C) 2007-2012  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdlib.h>
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <net/if.h>
31
32 #include <glib.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/log.h>
37 #include <connman/task.h>
38 #include <connman/ipconfig.h>
39
40 #include "../vpn-provider.h"
41
42 #include "vpn.h"
43
44 static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
45 {
46         DBusMessageIter iter, dict;
47         const char *reason, *key, *value;
48         const char *domain = NULL;
49         char *addressv4 = NULL, *addressv6 = NULL;
50         char *netmask = NULL, *gateway = NULL;
51         unsigned char prefix_len = 0;
52         struct connman_ipaddress *ipaddress;
53
54         dbus_message_iter_init(msg, &iter);
55
56         dbus_message_iter_get_basic(&iter, &reason);
57         dbus_message_iter_next(&iter);
58
59         if (!provider) {
60                 connman_error("No provider found");
61                 return VPN_STATE_FAILURE;
62         }
63
64         if (strcmp(reason, "connect"))
65                 return VPN_STATE_DISCONNECT;
66
67         domain = vpn_provider_get_string(provider, "VPN.Domain");
68
69         dbus_message_iter_recurse(&iter, &dict);
70
71         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
72                 DBusMessageIter entry;
73
74                 dbus_message_iter_recurse(&dict, &entry);
75                 dbus_message_iter_get_basic(&entry, &key);
76                 dbus_message_iter_next(&entry);
77                 dbus_message_iter_get_basic(&entry, &value);
78
79                 if (strcmp(key, "CISCO_CSTP_OPTIONS"))
80                         DBG("%s = %s", key, value);
81
82                 if (!strcmp(key, "VPNGATEWAY"))
83                         gateway = g_strdup(value);
84
85                 if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
86                         addressv4 = g_strdup(value);
87
88                 if (!strcmp(key, "INTERNAL_IP6_ADDRESS")) {
89                         addressv6 = g_strdup(value);
90                         prefix_len = 128;
91                 }
92
93                 if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
94                         netmask = g_strdup(value);
95
96                 if (!strcmp(key, "INTERNAL_IP6_NETMASK")) {
97                         char *sep;
98
99                         /* The netmask contains the address and the prefix */
100                         sep = strchr(value, '/');
101                         if (sep != NULL) {
102                                 unsigned char ip_len = sep - value;
103
104                                 addressv6 = g_strndup(value, ip_len);
105                                 prefix_len = (unsigned char)
106                                                 strtol(sep + 1, NULL, 10);
107                         }
108                 }
109
110                 if (!strcmp(key, "INTERNAL_IP4_DNS") ||
111                                 !strcmp(key, "INTERNAL_IP6_DNS"))
112                         vpn_provider_set_nameservers(provider, value);
113
114                 if (!strcmp(key, "CISCO_PROXY_PAC"))
115                         vpn_provider_set_pac(provider, value);
116
117                 if (domain == NULL && !strcmp(key, "CISCO_DEF_DOMAIN"))
118                         domain = value;
119
120                 if (g_str_has_prefix(key, "CISCO_SPLIT_INC") == TRUE ||
121                         g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC") == TRUE)
122                         vpn_provider_append_route(provider, key, value);
123
124                 dbus_message_iter_next(&dict);
125         }
126
127         DBG("%p %p", addressv4, addressv6);
128
129         if (addressv4 != NULL)
130                 ipaddress = connman_ipaddress_alloc(AF_INET);
131         else if (addressv6 != NULL)
132                 ipaddress = connman_ipaddress_alloc(AF_INET6);
133         else
134                 ipaddress = NULL;
135
136         if (ipaddress == NULL) {
137                 g_free(addressv4);
138                 g_free(addressv6);
139                 g_free(netmask);
140                 g_free(gateway);
141
142                 return VPN_STATE_FAILURE;
143         }
144
145         if (addressv4 != NULL)
146                 connman_ipaddress_set_ipv4(ipaddress, addressv4,
147                                                 netmask, gateway);
148         else
149                 connman_ipaddress_set_ipv6(ipaddress, addressv6,
150                                                 prefix_len, gateway);
151         vpn_provider_set_ipaddress(provider, ipaddress);
152         vpn_provider_set_domain(provider, domain);
153
154         g_free(addressv4);
155         g_free(addressv6);
156         g_free(netmask);
157         g_free(gateway);
158         connman_ipaddress_free(ipaddress);
159
160         return VPN_STATE_CONNECT;
161 }
162
163 static int oc_connect(struct vpn_provider *provider,
164                         struct connman_task *task, const char *if_name,
165                         vpn_provider_connect_cb_t cb, void *user_data)
166 {
167         const char *vpnhost, *vpncookie, *cafile, *certsha1, *mtu;
168         int fd, err = 0;
169
170         vpnhost = vpn_provider_get_string(provider, "Host");
171         if (!vpnhost) {
172                 connman_error("Host not set; cannot enable VPN");
173                 err = -EINVAL;
174                 goto done;
175         }
176
177         vpncookie = vpn_provider_get_string(provider, "OpenConnect.Cookie");
178         if (!vpncookie) {
179                 connman_error("OpenConnect.Cookie not set; cannot enable VPN");
180                 err = -EINVAL;
181                 goto done;
182         }
183
184         certsha1 = vpn_provider_get_string(provider,
185                                                 "OpenConnect.ServerCert");
186         if (certsha1)
187                 connman_task_add_argument(task, "--servercert",
188                                                         (char *)certsha1);
189
190         cafile = vpn_provider_get_string(provider, "OpenConnect.CACert");
191         mtu = vpn_provider_get_string(provider, "VPN.MTU");
192
193         if (cafile)
194                 connman_task_add_argument(task, "--cafile",
195                                                         (char *)cafile);
196         if (mtu)
197                 connman_task_add_argument(task, "--mtu", (char *)mtu);
198
199         connman_task_add_argument(task, "--syslog", NULL);
200         connman_task_add_argument(task, "--cookie-on-stdin", NULL);
201
202         connman_task_add_argument(task, "--script",
203                                   SCRIPTDIR "/openconnect-script");
204
205         connman_task_add_argument(task, "--interface", if_name);
206
207         connman_task_add_argument(task, (char *)vpnhost, NULL);
208
209         err = connman_task_run(task, vpn_died, provider,
210                                &fd, NULL, NULL);
211         if (err < 0) {
212                 connman_error("openconnect failed to start");
213                 err = -EIO;
214                 goto done;
215         }
216
217         if (write(fd, vpncookie, strlen(vpncookie)) !=
218                         (ssize_t)strlen(vpncookie) ||
219                         write(fd, "\n", 1) != 1) {
220                 connman_error("openconnect failed to take cookie on stdin");
221                 err = -EIO;
222                 goto done;
223         }
224
225 done:
226         if (cb != NULL)
227                 cb(provider, user_data, err);
228
229         return err;
230 }
231
232 static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
233 {
234         const char *setting;
235
236         setting = vpn_provider_get_string(provider,
237                                         "OpenConnect.ServerCert");
238         if (setting != NULL)
239                 g_key_file_set_string(keyfile,
240                                 vpn_provider_get_save_group(provider),
241                                 "OpenConnect.ServerCert", setting);
242
243         setting = vpn_provider_get_string(provider,
244                                         "OpenConnect.CACert");
245         if (setting != NULL)
246                 g_key_file_set_string(keyfile,
247                                 vpn_provider_get_save_group(provider),
248                                 "OpenConnect.CACert", setting);
249
250         setting = vpn_provider_get_string(provider,
251                                         "VPN.MTU");
252         if (setting != NULL)
253                 g_key_file_set_string(keyfile,
254                                 vpn_provider_get_save_group(provider),
255                                 "VPN.MTU", setting);
256
257         return 0;
258 }
259
260 static int oc_error_code(int exit_code)
261 {
262
263         switch (exit_code) {
264         case 1:
265                 return VPN_PROVIDER_ERROR_CONNECT_FAILED;
266         case 2:
267                 return VPN_PROVIDER_ERROR_LOGIN_FAILED;
268         default:
269                 return VPN_PROVIDER_ERROR_UNKNOWN;
270         }
271 }
272
273 static struct vpn_driver vpn_driver = {
274         .notify         = oc_notify,
275         .connect        = oc_connect,
276         .error_code     = oc_error_code,
277         .save           = oc_save,
278 };
279
280 static int openconnect_init(void)
281 {
282         return vpn_register("openconnect", &vpn_driver, OPENCONNECT);
283 }
284
285 static void openconnect_exit(void)
286 {
287         vpn_unregister("openconnect");
288 }
289
290 CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
291         CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)