openconnect: Domain pointer was used incorrectly
[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 #include <connman/dbus.h>
40 #include <connman/agent.h>
41 #include <connman/setting.h>
42 #include <connman/vpn-dbus.h>
43
44 #include "../vpn-provider.h"
45 #include "../vpn-agent.h"
46
47 #include "vpn.h"
48
49 struct oc_private_data {
50         struct connman_task *task;
51         char *if_name;
52         vpn_provider_connect_cb_t cb;
53         void *user_data;
54 };
55
56 static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
57 {
58         DBusMessageIter iter, dict;
59         const char *reason, *key, *value;
60         char *domain = NULL;
61         char *addressv4 = NULL, *addressv6 = NULL;
62         char *netmask = NULL, *gateway = NULL;
63         unsigned char prefix_len = 0;
64         struct connman_ipaddress *ipaddress;
65
66         dbus_message_iter_init(msg, &iter);
67
68         dbus_message_iter_get_basic(&iter, &reason);
69         dbus_message_iter_next(&iter);
70
71         if (!provider) {
72                 connman_error("No provider found");
73                 return VPN_STATE_FAILURE;
74         }
75
76         if (strcmp(reason, "connect"))
77                 return VPN_STATE_DISCONNECT;
78
79         domain = g_strdup(vpn_provider_get_string(provider, "VPN.Domain"));
80
81         dbus_message_iter_recurse(&iter, &dict);
82
83         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
84                 DBusMessageIter entry;
85
86                 dbus_message_iter_recurse(&dict, &entry);
87                 dbus_message_iter_get_basic(&entry, &key);
88                 dbus_message_iter_next(&entry);
89                 dbus_message_iter_get_basic(&entry, &value);
90
91                 if (strcmp(key, "CISCO_CSTP_OPTIONS"))
92                         DBG("%s = %s", key, value);
93
94                 if (!strcmp(key, "VPNGATEWAY"))
95                         gateway = g_strdup(value);
96
97                 if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
98                         addressv4 = g_strdup(value);
99
100                 if (!strcmp(key, "INTERNAL_IP6_ADDRESS")) {
101                         addressv6 = g_strdup(value);
102                         prefix_len = 128;
103                 }
104
105                 if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
106                         netmask = g_strdup(value);
107
108                 if (!strcmp(key, "INTERNAL_IP6_NETMASK")) {
109                         char *sep;
110
111                         /* The netmask contains the address and the prefix */
112                         sep = strchr(value, '/');
113                         if (sep != NULL) {
114                                 unsigned char ip_len = sep - value;
115
116                                 addressv6 = g_strndup(value, ip_len);
117                                 prefix_len = (unsigned char)
118                                                 strtol(sep + 1, NULL, 10);
119                         }
120                 }
121
122                 if (!strcmp(key, "INTERNAL_IP4_DNS") ||
123                                 !strcmp(key, "INTERNAL_IP6_DNS"))
124                         vpn_provider_set_nameservers(provider, value);
125
126                 if (!strcmp(key, "CISCO_PROXY_PAC"))
127                         vpn_provider_set_pac(provider, value);
128
129                 if (domain == NULL && !strcmp(key, "CISCO_DEF_DOMAIN")) {
130                         g_free(domain);
131                         domain = g_strdup(value);
132                 }
133
134                 if (g_str_has_prefix(key, "CISCO_SPLIT_INC") == TRUE ||
135                         g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC") == TRUE)
136                         vpn_provider_append_route(provider, key, value);
137
138                 dbus_message_iter_next(&dict);
139         }
140
141         DBG("%p %p", addressv4, addressv6);
142
143         if (addressv4 != NULL)
144                 ipaddress = connman_ipaddress_alloc(AF_INET);
145         else if (addressv6 != NULL)
146                 ipaddress = connman_ipaddress_alloc(AF_INET6);
147         else
148                 ipaddress = NULL;
149
150         if (ipaddress == NULL) {
151                 g_free(addressv4);
152                 g_free(addressv6);
153                 g_free(netmask);
154                 g_free(gateway);
155                 g_free(domain);
156
157                 return VPN_STATE_FAILURE;
158         }
159
160         if (addressv4 != NULL)
161                 connman_ipaddress_set_ipv4(ipaddress, addressv4,
162                                                 netmask, gateway);
163         else
164                 connman_ipaddress_set_ipv6(ipaddress, addressv6,
165                                                 prefix_len, gateway);
166         vpn_provider_set_ipaddress(provider, ipaddress);
167         vpn_provider_set_domain(provider, domain);
168
169         g_free(addressv4);
170         g_free(addressv6);
171         g_free(netmask);
172         g_free(gateway);
173         g_free(domain);
174         connman_ipaddress_free(ipaddress);
175
176         return VPN_STATE_CONNECT;
177 }
178
179 static void request_input_append_cookie(DBusMessageIter *iter,
180                                                         void *user_data)
181 {
182         char *str = "string";
183
184         connman_dbus_dict_append_basic(iter, "Type",
185                                 DBUS_TYPE_STRING, &str);
186         str = "mandatory";
187         connman_dbus_dict_append_basic(iter, "Requirement",
188                                 DBUS_TYPE_STRING, &str);
189 }
190
191 struct request_input_reply {
192         struct vpn_provider *provider;
193         vpn_provider_auth_cb_t callback;
194         void *user_data;
195 };
196
197 static void request_input_cookie_reply(DBusMessage *reply, void *user_data)
198 {
199         struct request_input_reply *cookie_reply = user_data;
200         const char *error = NULL;
201         char *cookie = NULL;
202         char *key;
203         DBusMessageIter iter, dict;
204
205         DBG("provider %p", cookie_reply->provider);
206
207         if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
208                 error = dbus_message_get_error_name(reply);
209                 goto done;
210         }
211
212         if (vpn_agent_check_reply_has_dict(reply) == FALSE)
213                 goto done;
214
215         dbus_message_iter_init(reply, &iter);
216         dbus_message_iter_recurse(&iter, &dict);
217         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
218                 DBusMessageIter entry, value;
219
220                 dbus_message_iter_recurse(&dict, &entry);
221                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
222                         break;
223
224                 dbus_message_iter_get_basic(&entry, &key);
225
226                 if (g_str_equal(key, "OpenConnect.Cookie")) {
227                         dbus_message_iter_next(&entry);
228                         if (dbus_message_iter_get_arg_type(&entry)
229                                                         != DBUS_TYPE_VARIANT)
230                                 break;
231                         dbus_message_iter_recurse(&entry, &value);
232                         if (dbus_message_iter_get_arg_type(&value)
233                                                         != DBUS_TYPE_STRING)
234                                 break;
235                         dbus_message_iter_get_basic(&value, &cookie);
236                 }
237
238                 dbus_message_iter_next(&dict);
239         }
240
241 done:
242         cookie_reply->callback(cookie_reply->provider, cookie, error,
243                                 cookie_reply->user_data);
244         g_free(cookie_reply);
245 }
246
247 typedef void (* request_cb_t)(struct vpn_provider *provider,
248                                         const char *vpncookie,
249                                         const char *error, void *user_data);
250
251 static int request_cookie_input(struct vpn_provider *provider,
252                                 request_cb_t callback, void *user_data)
253 {
254         DBusMessage *message;
255         const char *path, *agent_sender, *agent_path;
256         DBusMessageIter iter;
257         DBusMessageIter dict;
258         struct request_input_reply *cookie_reply;
259         int err;
260
261         connman_agent_get_info(&agent_sender, &agent_path);
262
263         if (provider == NULL || agent_path == NULL || callback == NULL)
264                 return -ESRCH;
265
266         message = dbus_message_new_method_call(agent_sender, agent_path,
267                                         VPN_AGENT_INTERFACE,
268                                         "RequestInput");
269         if (message == NULL)
270                 return -ENOMEM;
271
272         dbus_message_iter_init_append(message, &iter);
273
274         path = vpn_provider_get_path(provider);
275         dbus_message_iter_append_basic(&iter,
276                                 DBUS_TYPE_OBJECT_PATH, &path);
277
278         connman_dbus_dict_open(&iter, &dict);
279
280         connman_dbus_dict_append_dict(&dict, "OpenConnect.Cookie",
281                         request_input_append_cookie, provider);
282
283         vpn_agent_append_host_and_name(&dict, provider);
284
285         connman_dbus_dict_close(&iter, &dict);
286
287         cookie_reply = g_try_new0(struct request_input_reply, 1);
288         if (cookie_reply == NULL) {
289                 dbus_message_unref(message);
290                 return -ENOMEM;
291         }
292
293         cookie_reply->provider = provider;
294         cookie_reply->callback = callback;
295         cookie_reply->user_data = user_data;
296
297         err = connman_agent_queue_message(provider, message,
298                         connman_timeout_input_request(),
299                         request_input_cookie_reply, cookie_reply);
300         if (err < 0 && err != -EBUSY) {
301                 DBG("error %d sending agent request", err);
302                 dbus_message_unref(message);
303                 g_free(cookie_reply);
304                 return err;
305         }
306
307         dbus_message_unref(message);
308
309         return -EINPROGRESS;
310 }
311
312 static int run_connect(struct vpn_provider *provider,
313                         struct connman_task *task, const char *if_name,
314                         vpn_provider_connect_cb_t cb, void *user_data,
315                         const char *vpncookie)
316 {
317         const char *vpnhost, *cafile, *certsha1, *mtu;
318         int fd, err = 0, len;
319
320         vpnhost = vpn_provider_get_string(provider, "Host");
321
322         if (vpncookie == NULL) {
323                 DBG("Cookie missing, cannot connect!");
324                 err = -EINVAL;
325                 goto done;
326         }
327
328         vpn_provider_set_string(provider, "OpenConnect.Cookie", vpncookie);
329
330         certsha1 = vpn_provider_get_string(provider,
331                                                 "OpenConnect.ServerCert");
332         if (certsha1)
333                 connman_task_add_argument(task, "--servercert",
334                                                         (char *)certsha1);
335
336         cafile = vpn_provider_get_string(provider, "OpenConnect.CACert");
337         mtu = vpn_provider_get_string(provider, "VPN.MTU");
338
339         if (cafile)
340                 connman_task_add_argument(task, "--cafile",
341                                                         (char *)cafile);
342         if (mtu)
343                 connman_task_add_argument(task, "--mtu", (char *)mtu);
344
345         connman_task_add_argument(task, "--syslog", NULL);
346         connman_task_add_argument(task, "--cookie-on-stdin", NULL);
347
348         connman_task_add_argument(task, "--script",
349                                   SCRIPTDIR "/openconnect-script");
350
351         connman_task_add_argument(task, "--interface", if_name);
352
353         connman_task_add_argument(task, (char *)vpnhost, NULL);
354
355         err = connman_task_run(task, vpn_died, provider,
356                                &fd, NULL, NULL);
357         if (err < 0) {
358                 connman_error("openconnect failed to start");
359                 err = -EIO;
360                 goto done;
361         }
362
363         len = strlen(vpncookie);
364         if (write(fd, vpncookie, len) != (ssize_t)len ||
365                         write(fd, "\n", 1) != 1) {
366                 connman_error("openconnect failed to take cookie on stdin");
367                 err = -EIO;
368                 goto done;
369         }
370
371 done:
372         if (cb != NULL)
373                 cb(provider, user_data, err);
374
375         return err;
376 }
377
378 static void free_private_data(struct oc_private_data *data)
379 {
380         g_free(data->if_name);
381         g_free(data);
382 }
383
384 static void request_input_cb(struct vpn_provider *provider,
385                         const char *vpncookie,
386                         const char *error, void *user_data)
387 {
388         struct oc_private_data *data = user_data;
389
390         if (vpncookie == NULL)
391                 DBG("Requesting cookie failed, error %s", error);
392         else if (error != NULL)
393                 DBG("error %s", error);
394
395         run_connect(provider, data->task, data->if_name, data->cb,
396                 data->user_data, vpncookie);
397
398         free_private_data(data);
399 }
400
401 static int oc_connect(struct vpn_provider *provider,
402                         struct connman_task *task, const char *if_name,
403                         vpn_provider_connect_cb_t cb, void *user_data)
404 {
405         const char *vpnhost, *vpncookie;
406         int err;
407
408         vpnhost = vpn_provider_get_string(provider, "Host");
409         if (vpnhost == NULL) {
410                 connman_error("Host not set; cannot enable VPN");
411                 return -EINVAL;
412         }
413
414         vpncookie = vpn_provider_get_string(provider, "OpenConnect.Cookie");
415         if (vpncookie == NULL) {
416                 struct oc_private_data *data;
417
418                 data = g_try_new0(struct oc_private_data, 1);
419                 if (data == NULL)
420                         return -ENOMEM;
421
422                 data->task = task;
423                 data->if_name = g_strdup(if_name);
424                 data->cb = cb;
425                 data->user_data = user_data;
426
427                 err = request_cookie_input(provider, request_input_cb, data);
428                 if (err != -EINPROGRESS) {
429                         free_private_data(data);
430                         goto done;
431                 }
432                 return err;
433         }
434
435 done:
436         return run_connect(provider, task, if_name, cb, user_data, vpncookie);
437 }
438
439 static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
440 {
441         const char *setting;
442
443         setting = vpn_provider_get_string(provider,
444                                         "OpenConnect.ServerCert");
445         if (setting != NULL)
446                 g_key_file_set_string(keyfile,
447                                 vpn_provider_get_save_group(provider),
448                                 "OpenConnect.ServerCert", setting);
449
450         setting = vpn_provider_get_string(provider,
451                                         "OpenConnect.CACert");
452         if (setting != NULL)
453                 g_key_file_set_string(keyfile,
454                                 vpn_provider_get_save_group(provider),
455                                 "OpenConnect.CACert", setting);
456
457         setting = vpn_provider_get_string(provider,
458                                         "VPN.MTU");
459         if (setting != NULL)
460                 g_key_file_set_string(keyfile,
461                                 vpn_provider_get_save_group(provider),
462                                 "VPN.MTU", setting);
463
464         return 0;
465 }
466
467 static int oc_error_code(int exit_code)
468 {
469
470         switch (exit_code) {
471         case 1:
472                 return VPN_PROVIDER_ERROR_CONNECT_FAILED;
473         case 2:
474                 return VPN_PROVIDER_ERROR_LOGIN_FAILED;
475         default:
476                 return VPN_PROVIDER_ERROR_UNKNOWN;
477         }
478 }
479
480 static struct vpn_driver vpn_driver = {
481         .notify         = oc_notify,
482         .connect        = oc_connect,
483         .error_code     = oc_error_code,
484         .save           = oc_save,
485 };
486
487 static int openconnect_init(void)
488 {
489         return vpn_register("openconnect", &vpn_driver, OPENCONNECT);
490 }
491
492 static void openconnect_exit(void)
493 {
494         vpn_unregister("openconnect");
495 }
496
497 CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
498         CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)