Add support to get PMF information
[platform/upstream/connman.git] / vpn / plugins / vpnc.c
1 /*
2  *
3  *  ConnMan VPN daemon
4  *
5  *  Copyright (C) 2010,2013  BMW Car IT GmbH.
6  *  Copyright (C) 2010,2012-2013  Intel Corporation. All rights reserved.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License version 2 as
10  *  published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <string.h>
28 #include <errno.h>
29 #include <unistd.h>
30 #include <stdio.h>
31 #include <net/if.h>
32 #include <linux/if_tun.h>
33
34 #include <glib.h>
35
36 #define CONNMAN_API_SUBJECT_TO_CHANGE
37 #include <connman/plugin.h>
38 #include <connman/log.h>
39 #include <connman/task.h>
40 #include <connman/ipconfig.h>
41 #include <connman/dbus.h>
42 #include <connman/agent.h>
43 #include <connman/setting.h>
44 #include <connman/vpn-dbus.h>
45
46 #include "../vpn-provider.h"
47 #include "../vpn-agent.h"
48
49 #include "vpn.h"
50 #include "../vpn.h"
51
52 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
53
54 enum {
55         OPT_STRING = 1,
56         OPT_BOOLEAN = 2,
57 };
58
59 struct {
60         const char *cm_opt;
61         const char *vpnc_opt;
62         const char *vpnc_default;
63         int type;
64         bool cm_save;
65 } vpnc_options[] = {
66         { "Host", "IPSec gateway", NULL, OPT_STRING, true },
67         { "VPNC.IPSec.ID", "IPSec ID", NULL, OPT_STRING, true },
68         { "VPNC.IPSec.Secret", "IPSec secret", NULL, OPT_STRING, false },
69         { "VPNC.Xauth.Username", "Xauth username", NULL, OPT_STRING, false },
70         { "VPNC.Xauth.Password", "Xauth password", NULL, OPT_STRING, false },
71         { "VPNC.IKE.Authmode", "IKE Authmode", NULL, OPT_STRING, true },
72         { "VPNC.IKE.DHGroup", "IKE DH Group", NULL, OPT_STRING, true },
73         { "VPNC.PFS", "Perfect Forward Secrecy", NULL, OPT_STRING, true },
74         { "VPNC.Domain", "Domain", NULL, OPT_STRING, true },
75         { "VPNC.Vendor", "Vendor", NULL, OPT_STRING, true },
76         { "VPNC.LocalPort", "Local Port", "0", OPT_STRING, true, },
77         { "VPNC.CiscoPort", "Cisco UDP Encapsulation Port", "0", OPT_STRING,
78                                                                         true },
79         { "VPNC.AppVersion", "Application version", NULL, OPT_STRING, true },
80         { "VPNC.NATTMode", "NAT Traversal Mode", "cisco-udp", OPT_STRING,
81                                                                         true },
82         { "VPNC.DPDTimeout", "DPD idle timeout (our side)", NULL, OPT_STRING,
83                                                                         true },
84         { "VPNC.SingleDES", "Enable Single DES", NULL, OPT_BOOLEAN, true },
85         { "VPNC.NoEncryption", "Enable no encryption", NULL, OPT_BOOLEAN,
86                                                                         true },
87 };
88
89 struct vc_private_data {
90         struct vpn_provider *provider;
91         struct connman_task *task;
92         char *if_name;
93         vpn_provider_connect_cb_t cb;
94         void *user_data;
95         int err_ch_id;
96         GIOChannel *err_ch;
97 };
98
99 static void vc_connect_done(struct vc_private_data *data, int err)
100 {
101         DBG("data %p err %d", data, err);
102
103         if (data && data->cb) {
104                 vpn_provider_connect_cb_t cb = data->cb;
105                 void *user_data = data->user_data;
106
107                 /* Make sure we don't invoke this callback twice */
108                 data->cb = NULL;
109                 data->user_data = NULL;
110                 cb(data->provider, user_data, err);
111         }
112 }
113
114 static void close_io_channel(struct vc_private_data *data, GIOChannel *channel)
115 {
116         if (!data || !channel)
117                 return;
118
119         if (data->err_ch == channel) {
120                 DBG("closing stderr");
121
122                 if (data->err_ch_id) {
123                         g_source_remove(data->err_ch_id);
124                         data->err_ch_id = 0;
125                 }
126
127                 if (!data->err_ch)
128                         return;
129
130                 g_io_channel_shutdown(data->err_ch, FALSE, NULL);
131                 g_io_channel_unref(data->err_ch);
132
133                 data->err_ch = NULL;
134         }
135 }
136
137 static void free_private_data(struct vc_private_data *data)
138 {
139         DBG("data %p", data);
140
141         if (!data || !data->provider)
142                 return;
143
144         DBG("provider %p", data->provider);
145
146         if (vpn_provider_get_plugin_data(data->provider) == data)
147                 vpn_provider_set_plugin_data(data->provider, NULL);
148
149         vpn_provider_unref(data->provider);
150
151         g_free(data->if_name);
152         g_free(data);
153 }
154
155 static int vc_notify(DBusMessage *msg, struct vpn_provider *provider)
156 {
157         DBusMessageIter iter, dict;
158         char *address = NULL, *netmask = NULL, *gateway = NULL;
159         struct connman_ipaddress *ipaddress;
160         const char *reason, *key, *value;
161         struct vc_private_data *data;
162         int type;
163
164         data = vpn_provider_get_plugin_data(provider);
165
166         dbus_message_iter_init(msg, &iter);
167
168         type = dbus_message_iter_get_arg_type(&iter);
169         if (type != DBUS_TYPE_STRING) {
170                 DBG("invalid D-Bus arg type %d", type);
171                 return VPN_STATE_FAILURE;
172         }
173
174         dbus_message_iter_get_basic(&iter, &reason);
175         dbus_message_iter_next(&iter);
176
177         if (!provider) {
178                 connman_error("No provider found");
179                 vc_connect_done(data, ENOENT);
180                 return VPN_STATE_FAILURE;
181         }
182
183         if (g_strcmp0(reason, "connect")) {
184                 vc_connect_done(data, EIO);
185                 return VPN_STATE_DISCONNECT;
186         }
187
188         dbus_message_iter_recurse(&iter, &dict);
189
190         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
191                 DBusMessageIter entry;
192
193                 dbus_message_iter_recurse(&dict, &entry);
194
195                 type = dbus_message_iter_get_arg_type(&entry);
196                 if (type != DBUS_TYPE_STRING)
197                         continue;
198
199                 dbus_message_iter_get_basic(&entry, &key);
200                 dbus_message_iter_next(&entry);
201
202                 type = dbus_message_iter_get_arg_type(&entry);
203                 if (type != DBUS_TYPE_STRING)
204                         continue;
205
206                 dbus_message_iter_get_basic(&entry, &value);
207
208                 DBG("%s = %s", key, value);
209
210                 if (!strcmp(key, "VPNGATEWAY"))
211                         gateway = g_strdup(value);
212
213                 if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
214                         address = g_strdup(value);
215
216                 if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
217                         netmask = g_strdup(value);
218
219                 if (!strcmp(key, "INTERNAL_IP4_DNS"))
220                         vpn_provider_set_nameservers(provider, value);
221
222                 if (!strcmp(key, "CISCO_DEF_DOMAIN"))
223                         vpn_provider_set_domain(provider, value);
224
225                 if (g_str_has_prefix(key, "CISCO_SPLIT_INC") ||
226                         g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC"))
227                         vpn_provider_append_route(provider, key, value);
228
229                 dbus_message_iter_next(&dict);
230         }
231
232
233         ipaddress = connman_ipaddress_alloc(AF_INET);
234         if (!ipaddress) {
235                 g_free(address);
236                 g_free(netmask);
237                 g_free(gateway);
238                 vc_connect_done(data, EIO);
239                 return VPN_STATE_FAILURE;
240         }
241
242         connman_ipaddress_set_ipv4(ipaddress, address, netmask, gateway);
243         vpn_provider_set_ipaddress(provider, ipaddress);
244
245         g_free(address);
246         g_free(netmask);
247         g_free(gateway);
248         connman_ipaddress_free(ipaddress);
249
250         vc_connect_done(data, 0);
251         return VPN_STATE_CONNECT;
252 }
253
254 static ssize_t full_write(int fd, const void *buf, size_t len)
255 {
256         ssize_t byte_write;
257
258         while (len) {
259                 byte_write = write(fd, buf, len);
260                 if (byte_write < 0) {
261                         connman_error("failed to write config to vpnc: %s\n",
262                                         strerror(errno));
263                         return byte_write;
264                 }
265                 len -= byte_write;
266                 buf += byte_write;
267         }
268
269         return 0;
270 }
271
272 static ssize_t write_option(int fd, const char *key, const char *value)
273 {
274         gchar *buf;
275         ssize_t ret = 0;
276
277         if (key && value) {
278                 buf = g_strdup_printf("%s %s\n", key, value);
279                 ret = full_write(fd, buf, strlen(buf));
280
281                 g_free(buf);
282         }
283
284         return ret;
285 }
286
287 static ssize_t write_bool_option(int fd, const char *key, const char *value)
288 {
289         gchar *buf;
290         ssize_t ret = 0;
291
292         if (key && value) {
293                 if (strcasecmp(value, "yes") == 0 ||
294                                 strcasecmp(value, "true") == 0 ||
295                                 strcmp(value, "1") == 0) {
296                         buf = g_strdup_printf("%s\n", key);
297                         ret = full_write(fd, buf, strlen(buf));
298
299                         g_free(buf);
300                 }
301         }
302
303         return ret;
304 }
305
306 static int vc_write_config_data(struct vpn_provider *provider, int fd)
307 {
308         const char *opt_s;
309         int i;
310
311         for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
312                 opt_s = vpn_provider_get_string(provider,
313                                         vpnc_options[i].cm_opt);
314                 if (!opt_s)
315                         opt_s = vpnc_options[i].vpnc_default;
316
317                 if (!opt_s)
318                         continue;
319
320                 if (vpnc_options[i].type == OPT_STRING) {
321                         if (write_option(fd,
322                                         vpnc_options[i].vpnc_opt, opt_s) < 0)
323                                 return -EIO;
324                 } else if (vpnc_options[i].type == OPT_BOOLEAN) {
325                         if (write_bool_option(fd,
326                                         vpnc_options[i].vpnc_opt, opt_s) < 0)
327                                 return -EIO;
328                 }
329
330         }
331
332         return 0;
333 }
334
335 static int vc_save(struct vpn_provider *provider, GKeyFile *keyfile)
336 {
337         const char *option;
338         int i;
339
340         for (i = 0; i < (int)ARRAY_SIZE(vpnc_options); i++) {
341                 if (strncmp(vpnc_options[i].cm_opt, "VPNC.", 5) == 0) {
342
343                         if (!vpnc_options[i].cm_save)
344                                 continue;
345
346                         option = vpn_provider_get_string(provider,
347                                                         vpnc_options[i].cm_opt);
348                         if (!option)
349                                 continue;
350
351                         g_key_file_set_string(keyfile,
352                                         vpn_provider_get_save_group(provider),
353                                         vpnc_options[i].cm_opt, option);
354                 }
355         }
356         return 0;
357 }
358
359 static void vc_died(struct connman_task *task, int exit_code, void *user_data)
360 {
361         struct vc_private_data *data = user_data;
362
363         DBG("task %p data %p exit_code %d user_data %p", task, data, exit_code,
364                                 user_data);
365
366         if (!data)
367                 return;
368
369         if (data->provider) {
370                 connman_agent_cancel(data->provider);
371
372                 if (task)
373                         vpn_died(task, exit_code, data->provider);
374         }
375
376         free_private_data(data);
377 }
378
379 static gboolean io_channel_cb(GIOChannel *source, GIOCondition condition,
380                         gpointer user_data)
381 {
382         struct vc_private_data *data;
383         const char *auth_failures[] = {
384                         VPNC ": hash comparison failed",
385                         VPNC ": authentication unsuccessful",
386                         VPNC ": expected xauth packet; rejected",
387                         NULL
388         };
389         const char *conn_failures[] = {
390                         VPNC ": unknown host",
391                         VPNC ": no response from target",
392                         VPNC ": receiving packet: No route to host",
393                         NULL
394         };
395         char *str;
396         int i;
397
398         data = user_data;
399
400         if ((condition & G_IO_IN) &&
401                 g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
402                                                         G_IO_STATUS_NORMAL) {
403                 str[strlen(str) - 1] = '\0';
404
405                 for (i = 0; auth_failures[i]; i++) {
406                         if (g_str_has_prefix(str, auth_failures[i])) {
407                                 DBG("authentication failed: %s", str);
408
409                                 vpn_provider_indicate_error(data->provider,
410                                         VPN_PROVIDER_ERROR_AUTH_FAILED);
411                         }
412                 }
413
414                 for (i = 0; conn_failures[i]; i++) {
415                         if (g_str_has_prefix(str, conn_failures[i])) {
416                                 DBG("connection failed: %s", str);
417
418                                 vpn_provider_indicate_error(data->provider,
419                                         VPN_PROVIDER_ERROR_CONNECT_FAILED);
420                         }
421                 }
422
423                 g_free(str);
424         } else if (condition & (G_IO_ERR | G_IO_HUP)) {
425                 DBG("Channel termination");
426                 close_io_channel(data, source);
427                 return G_SOURCE_REMOVE;
428         }
429
430         return G_SOURCE_CONTINUE;
431 }
432
433 static int run_connect(struct vc_private_data *data)
434 {
435         struct vpn_provider *provider;
436         struct connman_task *task;
437         const char *credentials[] = {"VPNC.IPSec.Secret", "VPNC.Xauth.Username",
438                                 "VPNC.Xauth.Password", NULL};
439         const char *if_name;
440         const char *option;
441         int err;
442         int fd_in;
443         int fd_err;
444         int i;
445
446         provider = data->provider;
447         task = data->task;
448         if_name = data->if_name;
449
450         DBG("provider %p task %p interface %s user_data %p", provider, task,
451                                 if_name, data->user_data);
452
453         /*
454          * Change to use C locale, options should be in ASCII according to
455          * documentation. To be on the safe side, set both LANG and LC_ALL.
456          * This is required especially when the VPNC processe is ran using an
457          * user other than root.
458          */
459         connman_task_add_variable(task,"LANG", "C");
460         connman_task_add_variable(task,"LC_ALL", "C");
461
462         connman_task_add_argument(task, "--non-inter", NULL);
463         connman_task_add_argument(task, "--no-detach", NULL);
464
465         connman_task_add_argument(task, "--ifname", if_name);
466         option = vpn_provider_get_string(provider, "VPNC.DeviceType");
467         if (option) {
468                 connman_task_add_argument(task, "--ifmode", option);
469         } else {
470                 /*
471                  * Default to tun for backwards compatibility.
472                  */
473                 connman_task_add_argument(task, "--ifmode", "tun");
474         }
475
476         connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
477
478         option = vpn_provider_get_string(provider, "VPNC.Debug");
479         if (option)
480                 connman_task_add_argument(task, "--debug", option);
481
482         connman_task_add_argument(task, "-", NULL);
483
484         err = connman_task_run(data->task, vc_died, data, &fd_in, NULL,
485                                 &fd_err);
486         if (err < 0) {
487                 connman_error("vpnc failed to start");
488                 err = -EIO;
489                 goto done;
490         }
491
492         err = vc_write_config_data(provider, fd_in);
493
494         if (err) {
495                 DBG("config write error %s", strerror(err));
496                 goto done;
497         }
498
499         err = -EINPROGRESS;
500
501         data->err_ch = g_io_channel_unix_new(fd_err);
502         data->err_ch_id = g_io_add_watch(data->err_ch,
503                                 G_IO_IN | G_IO_ERR | G_IO_HUP,
504                                 (GIOFunc)io_channel_cb, data);
505
506 done:
507         close(fd_in);
508
509         /*
510          * Clear out credentials if they are non-immutable. If this is called
511          * directly from vc_connect() all credentials are read from config and
512          * are set as immutable, so no change is done. In case a VPN agent is
513          * used these values should be reset to "-" in order to retrieve them
514          * from VPN agent next time VPN connection is established. This supports
515          * then partially defined credentials in .config and some can be
516          * retrieved using an agent.
517          */
518         for (i = 0; credentials[i]; i++) {
519                 const char *key = credentials[i];
520                 if (!vpn_provider_get_string_immutable(provider, key))
521                         vpn_provider_set_string(provider, key, "-");
522         }
523
524         return err;
525 }
526
527 static void request_input_append_mandatory(DBusMessageIter *iter,
528                 void *user_data)
529 {
530         char *str = "string";
531
532         connman_dbus_dict_append_basic(iter, "Type",
533                                 DBUS_TYPE_STRING, &str);
534         str = "mandatory";
535         connman_dbus_dict_append_basic(iter, "Requirement",
536                                 DBUS_TYPE_STRING, &str);
537
538         if (!user_data)
539                 return;
540
541         str = user_data;
542         connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING, &str);
543 }
544
545 static void request_input_append_password(DBusMessageIter *iter,
546                 void *user_data)
547 {
548         char *str = "password";
549
550         connman_dbus_dict_append_basic(iter, "Type",
551                                 DBUS_TYPE_STRING, &str);
552         str = "mandatory";
553         connman_dbus_dict_append_basic(iter, "Requirement",
554                                 DBUS_TYPE_STRING, &str);
555
556         if (!user_data)
557                 return;
558
559         str = user_data;
560         connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING, &str);
561 }
562
563 static void request_input_append_informational(DBusMessageIter *iter,
564                 void *user_data)
565 {
566         char *str = "password";
567
568         connman_dbus_dict_append_basic(iter, "Type",
569                                 DBUS_TYPE_STRING, &str);
570         str = "informational";
571         connman_dbus_dict_append_basic(iter, "Requirement",
572                                 DBUS_TYPE_STRING, &str);
573
574         if (!user_data)
575                 return;
576
577         str = user_data;
578         connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING, &str);
579 }
580
581 static void request_input_append_to_dict(struct vpn_provider *provider,
582                         DBusMessageIter *dict,
583                         connman_dbus_append_cb_t function_cb, const char *key)
584 {
585         const char *str;
586         bool immutable = false;
587
588         if (!provider || !dict || !function_cb || !key)
589                 return;
590
591         str = vpn_provider_get_string(provider, key);
592
593         /* If value is "-", it is cleared by VPN agent */
594         if (!g_strcmp0(str, "-"))
595                 str = NULL;
596
597         if (str)
598                 immutable = vpn_provider_get_string_immutable(provider, key);
599
600         if (immutable) {
601                 /* Hide immutable password types */
602                 if (function_cb == request_input_append_password)
603                         str = "********";
604
605                 /* Send immutable as informational */
606                 function_cb = request_input_append_informational;
607         }
608
609         connman_dbus_dict_append_dict(dict, key, function_cb, (void *)str);
610 }
611
612 static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
613 {
614         struct vc_private_data *data = user_data;
615         char *secret = NULL, *username = NULL, *password = NULL;
616         const char *key;
617         DBusMessageIter iter, dict;
618         int err;
619
620         DBG("provider %p", data->provider);
621
622         if (!reply)
623                 goto err;
624
625         err = vpn_agent_check_and_process_reply_error(reply, data->provider,
626                                 data->task, data->cb, data->user_data);
627         if (err) {
628                 /* Ensure cb is called only once */
629                 data->cb = NULL;
630                 data->user_data = NULL;
631                 return;
632         }
633
634         if (!vpn_agent_check_reply_has_dict(reply))
635                 goto err;
636
637         dbus_message_iter_init(reply, &iter);
638         dbus_message_iter_recurse(&iter, &dict);
639         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
640                 DBusMessageIter entry, value;
641
642                 dbus_message_iter_recurse(&dict, &entry);
643                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
644                         break;
645
646                 dbus_message_iter_get_basic(&entry, &key);
647
648                 if (g_str_equal(key, "VPNC.IPSec.Secret")) {
649                         dbus_message_iter_next(&entry);
650                         if (dbus_message_iter_get_arg_type(&entry)
651                                                         != DBUS_TYPE_VARIANT)
652                                 break;
653                         dbus_message_iter_recurse(&entry, &value);
654                         if (dbus_message_iter_get_arg_type(&value)
655                                                         != DBUS_TYPE_STRING)
656                                 break;
657                         dbus_message_iter_get_basic(&value, &secret);
658                         vpn_provider_set_string_hide_value(data->provider,
659                                         key, secret);
660
661                 } else if (g_str_equal(key, "VPNC.Xauth.Username")) {
662                         dbus_message_iter_next(&entry);
663                         if (dbus_message_iter_get_arg_type(&entry)
664                                                         != DBUS_TYPE_VARIANT)
665                                 break;
666                         dbus_message_iter_recurse(&entry, &value);
667                         if (dbus_message_iter_get_arg_type(&value)
668                                                         != DBUS_TYPE_STRING)
669                                 break;
670                         dbus_message_iter_get_basic(&value, &username);
671                         vpn_provider_set_string(data->provider, key, username);
672
673                 } else if (g_str_equal(key, "VPNC.Xauth.Password")) {
674                         dbus_message_iter_next(&entry);
675                         if (dbus_message_iter_get_arg_type(&entry)
676                                                         != DBUS_TYPE_VARIANT)
677                                 break;
678                         dbus_message_iter_recurse(&entry, &value);
679                         if (dbus_message_iter_get_arg_type(&value)
680                                                         != DBUS_TYPE_STRING)
681                                 break;
682                         dbus_message_iter_get_basic(&value, &password);
683                         vpn_provider_set_string_hide_value(data->provider, key,
684                                                 password);
685                 }
686
687                 dbus_message_iter_next(&dict);
688         }
689
690         if (!secret || !username || !password)
691                 goto err;
692
693         err = run_connect(data);
694         if (err != -EINPROGRESS)
695                 goto err;
696
697         return;
698
699 err:
700         vc_connect_done(data, EACCES);
701 }
702
703 static int request_input_credentials(struct vc_private_data *data,
704                                         const char* dbus_sender)
705 {
706         DBusMessage *message;
707         const char *path, *agent_sender, *agent_path;
708         DBusMessageIter iter;
709         DBusMessageIter dict;
710         int err;
711         void *agent;
712
713         if (!data || !data->provider)
714                 return -ENOENT;
715
716         DBG("data %p provider %p sender %s", data, data->provider, dbus_sender);
717
718         agent = connman_agent_get_info(dbus_sender, &agent_sender, &agent_path);
719         if (!agent || !agent_path)
720                 return -ESRCH;
721
722         message = dbus_message_new_method_call(agent_sender, agent_path,
723                                         VPN_AGENT_INTERFACE,
724                                         "RequestInput");
725         if (!message)
726                 return -ENOMEM;
727
728         dbus_message_iter_init_append(message, &iter);
729
730         path = vpn_provider_get_path(data->provider);
731         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
732
733         connman_dbus_dict_open(&iter, &dict);
734
735         if (vpn_provider_get_authentication_errors(data->provider))
736                 vpn_agent_append_auth_failure(&dict, data->provider, NULL);
737
738         request_input_append_to_dict(data->provider, &dict,
739                                 request_input_append_password,
740                                 "VPNC.IPSec.Secret");
741         request_input_append_to_dict(data->provider, &dict,
742                                 request_input_append_mandatory,
743                                 "VPNC.Xauth.Username");
744         request_input_append_to_dict(data->provider, &dict,
745                                 request_input_append_password,
746                                 "VPNC.Xauth.Password");
747
748         vpn_agent_append_host_and_name(&dict, data->provider);
749
750         connman_dbus_dict_close(&iter, &dict);
751
752         err = connman_agent_queue_message(data->provider, message,
753                                 connman_timeout_input_request(),
754                                 request_input_credentials_reply, data, agent);
755
756         dbus_message_unref(message);
757
758         if (err < 0 && err != -EBUSY) {
759                 DBG("error %d sending agent request", err);
760                 return err;
761         }
762
763         return -EINPROGRESS;
764 }
765
766 static int vc_connect(struct vpn_provider *provider,
767                         struct connman_task *task, const char *if_name,
768                         vpn_provider_connect_cb_t cb, const char *dbus_sender,
769                         void *user_data)
770 {
771         struct vc_private_data *data;
772         const char *option;
773         bool username_set = false;
774         bool password_set = false;
775         bool ipsec_secret_set = false;
776         int err;
777
778         DBG("provider %p if_name %s user_data %p", provider, if_name, user_data);
779
780         option = vpn_provider_get_string(provider, "VPNC.IPSec.ID");
781         if (!option) {
782                 connman_error("Group not set; cannot enable VPN");
783                 return -EINVAL;
784         }
785
786         option = vpn_provider_get_string(provider, "VPNC.IPSec.Secret");
787         if (option && *option && g_strcmp0(option, "-"))
788                 ipsec_secret_set = true;
789
790         option = vpn_provider_get_string(provider, "VPNC.Xauth.Username");
791         if (option && *option && g_strcmp0(option, "-"))
792                 username_set = true;
793
794         option = vpn_provider_get_string(provider, "VPNC.Xauth.Password");
795         if (option && *option && g_strcmp0(option, "-"))
796                 password_set = true;
797
798         data = g_try_new0(struct vc_private_data, 1);
799         if (!data)
800                 return -ENOMEM;
801
802         vpn_provider_set_plugin_data(provider, data);
803         data->provider = vpn_provider_ref(provider);
804         data->task = task;
805         data->if_name = g_strdup(if_name);
806         data->cb = cb;
807         data->user_data = user_data;
808
809         if (!ipsec_secret_set || !username_set || !password_set) {
810                 err = request_input_credentials(data, dbus_sender);
811                 if (err != -EINPROGRESS) {
812                         vc_connect_done(data, ECONNABORTED);
813                         vpn_provider_indicate_error(data->provider,
814                                         VPN_PROVIDER_ERROR_LOGIN_FAILED);
815                         free_private_data(data);
816                 }
817
818                 return err;
819         }
820
821         return run_connect(data);
822 }
823
824 static void vc_disconnect(struct vpn_provider *provider)
825 {
826         if (!provider)
827                 return;
828
829         connman_agent_cancel(provider);
830 }
831
832 static int vc_error_code(struct vpn_provider *provider, int exit_code)
833 {
834         switch (exit_code) {
835         case 1:
836                 return VPN_PROVIDER_ERROR_CONNECT_FAILED;
837         case 2:
838                 return VPN_PROVIDER_ERROR_LOGIN_FAILED;
839         default:
840                 return VPN_PROVIDER_ERROR_UNKNOWN;
841         }
842 }
843
844 static int vc_device_flags(struct vpn_provider *provider)
845 {
846         const char *option;
847
848         option = vpn_provider_get_string(provider, "VPNC.DeviceType");
849         if (!option) {
850                 return IFF_TUN;
851         }
852
853         if (g_str_equal(option, "tap")) {
854                 return IFF_TAP;
855         }
856
857         if (!g_str_equal(option, "tun")) {
858                 connman_warn("bad VPNC.DeviceType value, falling back to tun");
859         }
860
861         return IFF_TUN;
862 }
863
864 static struct vpn_driver vpn_driver = {
865         .notify         = vc_notify,
866         .connect        = vc_connect,
867         .disconnect     = vc_disconnect,
868         .error_code     = vc_error_code,
869         .save           = vc_save,
870         .device_flags   = vc_device_flags,
871 };
872
873 static int vpnc_init(void)
874 {
875         return vpn_register("vpnc", &vpn_driver, VPNC);
876 }
877
878 static void vpnc_exit(void)
879 {
880         vpn_unregister("vpnc");
881 }
882
883 CONNMAN_PLUGIN_DEFINE(vpnc, "vpnc plugin", VERSION,
884         CONNMAN_PLUGIN_PRIORITY_DEFAULT, vpnc_init, vpnc_exit)