Imported Upstream version 1.40
[platform/upstream/connman.git] / vpn / plugins / openconnect.c
1 /*
2  *
3  *  ConnMan VPN daemon
4  *
5  *  Copyright (C) 2007-2013  Intel Corporation. All rights reserved.
6  *  Copyright (C) 2019  Jolla Ltd. 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 <stdlib.h>
28 #include <string.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <net/if.h>
32
33 #include <glib.h>
34
35 #define CONNMAN_API_SUBJECT_TO_CHANGE
36 #include <connman/plugin.h>
37 #include <connman/log.h>
38 #include <connman/task.h>
39 #include <connman/ipconfig.h>
40 #include <connman/dbus.h>
41 #include <connman/agent.h>
42 #include <connman/setting.h>
43 #include <connman/vpn-dbus.h>
44
45 #include <openconnect.h>
46
47 #include "../vpn-provider.h"
48 #include "../vpn-agent.h"
49
50 #include "vpn.h"
51
52 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
53 #define OC_MAX_READBUF_LEN 128
54
55 enum opt_type {
56         OPT_STRING      = 0,
57         OPT_BOOL        = 1,
58 };
59
60 struct {
61         const char      *cm_opt;
62         const char      *oc_opt;
63         bool            has_value;
64         bool            enabled; // Use as task parameter
65         enum opt_type   type;
66 } oc_options[] = {
67         { "OpenConnect.AllowSelfSignedCert", NULL, 1, 0, OPT_BOOL},
68         { "OpenConnect.AuthType", NULL, 1, 0, OPT_STRING},
69         { "OpenConnect.CACert", "--cafile", 1, 1, OPT_STRING},
70         { "OpenConnect.ClientCert", NULL, 1, 0, OPT_STRING},
71         { "OpenConnect.DisableIPv6", "--disable-ipv6", 1, 1, OPT_BOOL},
72         { "OpenConnect.PKCSClientCert", NULL, 1, 0, OPT_STRING},
73         { "OpenConnect.Protocol", "--protocol", 1, 1, OPT_STRING},
74         /* --no-cert-check is disabled in openconnect 8.02 */
75         { "OpenConnect.NoCertCheck", "--no-cert-check", 0, 0, OPT_BOOL},
76         { "OpenConnect.NoHTTPKeepalive", "--no-http-keepalive", 1, 1, OPT_BOOL},
77         { "OpenConnect.NoDTLS", "--no-dtls", 1, 1, OPT_BOOL},
78         { "OpenConnect.ServerCert", "--servercert", 1, 1, OPT_STRING},
79         { "OpenConnect.Usergroup", "--usergroup", 1, 1, OPT_STRING},
80         { "OpenConnect.UserPrivateKey", NULL, 1, 0, OPT_STRING},
81         { "VPN.MTU", "--base-mtu", 1, 1, OPT_STRING},
82 };
83
84 enum oc_connect_type {
85         OC_CONNECT_COOKIE = 0,
86         OC_CONNECT_COOKIE_WITH_USERPASS,
87         OC_CONNECT_USERPASS,
88         OC_CONNECT_PUBLICKEY,
89         OC_CONNECT_PKCS,
90 };
91
92 static const char *connect_types[] = {"cookie", "cookie_with_userpass",
93                         "userpass", "publickey", "pkcs", NULL};
94
95 struct oc_private_data {
96         struct vpn_provider *provider;
97         struct connman_task *task;
98         char *if_name;
99         char *dbus_sender;
100         vpn_provider_connect_cb_t cb;
101         void *user_data;
102
103         GThread *cookie_thread;
104         struct openconnect_info *vpninfo;
105         int fd_cmd;
106         int err;
107
108         int fd_in;
109         int err_ch_id;
110         GIOChannel *err_ch;
111         enum oc_connect_type connect_type;
112         bool tried_passphrase;
113 };
114
115 typedef void (*request_input_reply_cb_t) (DBusMessage *reply,
116                                         void *user_data);
117
118 static int run_connect(struct oc_private_data *data, const char *cookie);
119 static int request_input_credentials_full(
120                         struct oc_private_data *data,
121                         request_input_reply_cb_t cb,
122                         void *user_data);
123
124 static bool is_valid_protocol(const char* protocol)
125 {
126         int num_protocols;
127         int i;
128         struct oc_vpn_proto *protos;
129
130         if (!protocol || !*protocol)
131                 return false;
132
133         num_protocols = openconnect_get_supported_protocols(&protos);
134
135         for (i = 0; i < num_protocols; i++)
136                 if (!strcmp(protos[i].name, protocol))
137                         break;
138
139         openconnect_free_supported_protocols(protos);
140
141         return i < num_protocols;
142 }
143
144 static void oc_connect_done(struct oc_private_data *data, int err)
145 {
146         connman_info("data %p err %d/%s", data, err, strerror(err));
147
148         if (data && data->cb) {
149                 vpn_provider_connect_cb_t cb = data->cb;
150                 void *user_data = data->user_data;
151
152                 /* Make sure we don't invoke this callback twice */
153                 data->cb = NULL;
154                 data->user_data = NULL;
155                 cb(data->provider, user_data, err);
156         }
157 }
158
159 static void close_io_channel(struct oc_private_data *data, GIOChannel *channel)
160 {
161         int id = 0;
162
163         connman_info("data %p channel %p", data, channel);
164
165         if (!data || !channel)
166                 return;
167
168         if (data->err_ch == channel) {
169                 id = data->err_ch_id;
170                 data->err_ch = NULL;
171                 data->err_ch_id = 0;
172         } else {
173                 return;
174         }
175
176         if (id)
177                 g_source_remove(id);
178
179         g_io_channel_shutdown(channel, FALSE, NULL);
180         g_io_channel_unref(channel);
181 }
182
183 static void free_private_data(struct oc_private_data *data)
184 {
185         connman_info("data %p", data);
186
187         if (!data || !data->provider)
188                 return;
189
190         connman_info("provider %p", data->provider);
191
192         if (data->vpninfo)
193                 openconnect_vpninfo_free(data->vpninfo);
194
195         if (vpn_provider_get_plugin_data(data->provider) == data)
196                 vpn_provider_set_plugin_data(data->provider, NULL);
197
198         vpn_provider_unref(data->provider);
199
200         if (data->fd_in > 0)
201                 close(data->fd_in);
202         data->fd_in = -1;
203         close_io_channel(data, data->err_ch);
204
205         g_free(data->dbus_sender);
206         g_free(data->if_name);
207         g_free(data);
208 }
209
210 static int task_append_config_data(struct vpn_provider *provider,
211                                         struct connman_task *task)
212 {
213         const char *option = NULL;
214         int i;
215
216         for (i = 0; i < (int)ARRAY_SIZE(oc_options); i++) {
217                 if (!oc_options[i].oc_opt || !oc_options[i].enabled)
218                         continue;
219
220                 if (oc_options[i].has_value) {
221                         option = vpn_provider_get_string(provider,
222                                                 oc_options[i].cm_opt);
223                         if (!option)
224                                 continue;
225
226                         /* Add boolean type values only if set as true. */
227                         if (oc_options[i].type == OPT_BOOL) {
228                                 if (!vpn_provider_get_boolean(provider,
229                                                         oc_options[i].cm_opt,
230                                                         false))
231                                         continue;
232
233                                 /* No option is set for boolean type values. */
234                                 option = NULL;
235                         }
236
237                         /* Skip protocol if it is invalid. */
238                         if (!g_strcmp0(oc_options[i].cm_opt,
239                                                 "OpenConnect.Protocol")) {
240                                 if (!is_valid_protocol(option))
241                                         continue;
242                         }
243                 }
244
245                 /*
246                  * Add server certificate fingerprint only when self signed
247                  * certificates are explicitly allowed. Using --servercert as
248                  * parameter will accept any server with matching fingerprint,
249                  * which would disregard the setting of AllowSelfSignedCert.
250                  */
251                 if (!g_strcmp0(oc_options[i].cm_opt,
252                                         "OpenConnect.ServerCert")) {
253                         if (!vpn_provider_get_boolean(provider,
254                                         "OpenConnect.AllowSelfSignedCert",
255                                         false))
256                                 continue;
257                 }
258
259                 if (connman_task_add_argument(task,
260                                 oc_options[i].oc_opt,
261                                 oc_options[i].has_value ? option : NULL) < 0)
262                         return -EIO;
263         }
264
265         return 0;
266 }
267
268 static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
269 {
270         DBusMessageIter iter, dict;
271         const char *reason, *key, *value;
272         char *domain = NULL;
273         char *addressv4 = NULL, *addressv6 = NULL;
274         char *netmask = NULL, *gateway = NULL;
275         unsigned char prefix_len = 0;
276         struct connman_ipaddress *ipaddress;
277         struct oc_private_data *data;
278
279         connman_info("provider %p", provider);
280
281         data = vpn_provider_get_plugin_data(provider);
282
283         dbus_message_iter_init(msg, &iter);
284
285         dbus_message_iter_get_basic(&iter, &reason);
286         dbus_message_iter_next(&iter);
287
288         if (!provider) {
289                 connman_error("No provider found");
290                 oc_connect_done(data, ENOENT);
291                 return VPN_STATE_FAILURE;
292         }
293
294         if (strcmp(reason, "connect"))
295                 return VPN_STATE_DISCONNECT;
296
297         domain = g_strdup(vpn_provider_get_string(provider, "VPN.Domain"));
298
299         dbus_message_iter_recurse(&iter, &dict);
300
301         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
302                 DBusMessageIter entry;
303
304                 dbus_message_iter_recurse(&dict, &entry);
305                 dbus_message_iter_get_basic(&entry, &key);
306                 dbus_message_iter_next(&entry);
307                 dbus_message_iter_get_basic(&entry, &value);
308
309                 if (strcmp(key, "CISCO_CSTP_OPTIONS"))
310                         DBG("%s = %s", key, value);
311
312                 if (!strcmp(key, "VPNGATEWAY"))
313                         gateway = g_strdup(value);
314
315                 if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
316                         addressv4 = g_strdup(value);
317
318                 if (!strcmp(key, "INTERNAL_IP6_ADDRESS")) {
319                         addressv6 = g_strdup(value);
320                         prefix_len = 128;
321                 }
322
323                 if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
324                         netmask = g_strdup(value);
325
326                 if (!strcmp(key, "INTERNAL_IP6_NETMASK")) {
327                         char *sep;
328
329                         /* The netmask contains the address and the prefix */
330                         sep = strchr(value, '/');
331                         if (sep) {
332                                 unsigned char ip_len = sep - value;
333
334                                 addressv6 = g_strndup(value, ip_len);
335                                 prefix_len = (unsigned char)
336                                                 strtol(sep + 1, NULL, 10);
337                         }
338                 }
339
340                 if (!strcmp(key, "INTERNAL_IP4_DNS") ||
341                                 !strcmp(key, "INTERNAL_IP6_DNS"))
342                         vpn_provider_set_nameservers(provider, value);
343
344                 if (!strcmp(key, "CISCO_PROXY_PAC"))
345                         vpn_provider_set_pac(provider, value);
346
347                 if (!domain && !strcmp(key, "CISCO_DEF_DOMAIN")) {
348                         g_free(domain);
349                         domain = g_strdup(value);
350                 }
351
352                 if (g_str_has_prefix(key, "CISCO_SPLIT_INC") ||
353                         g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC"))
354                         vpn_provider_append_route(provider, key, value);
355
356                 dbus_message_iter_next(&dict);
357         }
358
359         DBG("%p %p", addressv4, addressv6);
360
361         if (addressv4)
362                 ipaddress = connman_ipaddress_alloc(AF_INET);
363         else if (addressv6)
364                 ipaddress = connman_ipaddress_alloc(AF_INET6);
365         else
366                 ipaddress = NULL;
367
368         if (!ipaddress) {
369                 g_free(addressv4);
370                 g_free(addressv6);
371                 g_free(netmask);
372                 g_free(gateway);
373                 g_free(domain);
374
375                 return VPN_STATE_FAILURE;
376         }
377
378         if (addressv4)
379                 connman_ipaddress_set_ipv4(ipaddress, addressv4,
380                                                 netmask, gateway);
381         else
382                 connman_ipaddress_set_ipv6(ipaddress, addressv6,
383                                                 prefix_len, gateway);
384
385         connman_ipaddress_set_p2p(ipaddress, true);
386         vpn_provider_set_ipaddress(provider, ipaddress);
387         vpn_provider_set_domain(provider, domain);
388
389         g_free(addressv4);
390         g_free(addressv6);
391         g_free(netmask);
392         g_free(gateway);
393         g_free(domain);
394         connman_ipaddress_free(ipaddress);
395
396         oc_connect_done(data, 0);
397         return VPN_STATE_CONNECT;
398 }
399
400 static ssize_t full_write(int fd, const char *buf, size_t len)
401 {
402         ssize_t byte_write;
403
404         while (len) {
405                 byte_write = write(fd, buf, len);
406                 if (byte_write < 0) {
407                         connman_error("failed to write config to openconnect: "
408                                         " %s\n", strerror(errno));
409                         return byte_write;
410                 }
411                 len -= byte_write;
412                 buf += byte_write;
413         }
414
415         return len;
416 }
417
418 static ssize_t write_data(int fd, const char *data)
419 {
420         gchar *buf;
421         ssize_t len;
422
423         if (!data || !*data)
424                 return -1;
425
426         buf = g_strdup_printf("%s\n", data);
427
428         len = full_write(fd, buf, strlen(buf));
429
430         g_free(buf);
431
432         return len;
433 }
434
435 static void oc_died(struct connman_task *task, int exit_code, void *user_data)
436 {
437         struct oc_private_data *data = user_data;
438
439         connman_info("task %p data %p exit_code %d user_data %p", task, data,
440                                 exit_code, user_data);
441
442         if (!data)
443                 return;
444
445         if (data->provider) {
446                 connman_agent_cancel(data->provider);
447
448                 if (task)
449                         vpn_died(task, exit_code, data->provider);
450         }
451
452         free_private_data(data);
453 }
454
455 static bool strv_contains_prefix(const char *strv[], const char *str)
456 {
457         int i;
458
459         if (!strv || !str || !*str)
460                 return false;
461
462         for (i = 0; strv[i]; i++) {
463                 if (g_str_has_prefix(str, strv[i]))
464                         return true;
465         }
466
467         return false;
468 }
469
470 static void clear_provider_credentials(struct vpn_provider *provider,
471                                                 bool clear_pkcs_pass)
472 {
473         const char *keys[] = { "OpenConnect.PKCSPassword",
474                                 "OpenConnect.Username",
475                                 "OpenConnect.Password",
476                                 "OpenConnect.Cookie",
477                                 NULL
478         };
479         size_t i;
480
481         connman_info("provider %p", provider);
482
483         for (i = !clear_pkcs_pass; keys[i]; i++) {
484                 if (!vpn_provider_get_string_immutable(provider, keys[i]))
485                         vpn_provider_set_string_hide_value(provider, keys[i],
486                                                 "-");
487         }
488 }
489
490 static void __attribute__ ((format(printf, 3, 4))) oc_progress(void *user_data,
491                 int level, const char *fmt, ...)
492 {
493         va_list ap;
494         char *msg;
495
496         va_start(ap, fmt);
497         msg = g_strdup_vprintf(fmt, ap);
498
499         connman_debug("%s", msg);
500         g_free(msg);
501
502         va_end(ap);
503 }
504
505 /*
506  * There is no enum / defines for these in openconnect.h, but these values
507  * are based on the comment for openconnect_validate_peer_cert_vfn.
508  */
509 enum oc_cert_status {
510         OC_CERT_ACCEPT = 0,
511         OC_CERT_REJECT = 1
512 };
513
514 struct validate_cert_data {
515         GMutex mutex;
516         GCond cond;
517         const char *reason;
518         struct oc_private_data *data;
519         bool processed;
520         enum oc_cert_status status;
521 };
522
523 static gboolean validate_cert(void *user_data)
524 {
525         struct validate_cert_data *cert_data = user_data;
526         struct oc_private_data *data;
527         const char *server_cert;
528         bool allow_self_signed;
529
530         DBG("");
531
532         g_mutex_lock(&cert_data->mutex);
533
534         data = cert_data->data;
535         server_cert = vpn_provider_get_string(data->provider,
536                                                 "OpenConnect.ServerCert");
537         allow_self_signed = vpn_provider_get_boolean(data->provider,
538                                         "OpenConnect.AllowSelfSignedCert",
539                                         false);
540
541         if (!allow_self_signed) {
542                 cert_data->status = OC_CERT_REJECT;
543         } else if (server_cert) {
544                 /*
545                  * Check peer cert hash may return negative values on errors,
546                  * but anything non-zero is acceptable.
547                  */
548                 cert_data->status = openconnect_check_peer_cert_hash(
549                                                                 data->vpninfo,
550                                                                 server_cert);
551         } else {
552                 /*
553                  * We could verify this from the agent at this point, and
554                  * release the thread upon reply.
555                  */
556                 DBG("Server cert hash: %s",
557                                 openconnect_get_peer_cert_hash(data->vpninfo));
558                 vpn_provider_set_string(data->provider,
559                                 "OpenConnect.ServerCert",
560                                 openconnect_get_peer_cert_hash(data->vpninfo));
561                 cert_data->status = OC_CERT_ACCEPT;
562         }
563
564         cert_data->processed = true;
565         g_cond_signal(&cert_data->cond);
566         g_mutex_unlock(&cert_data->mutex);
567
568         return G_SOURCE_REMOVE;
569 }
570
571 static int oc_validate_peer_cert(void *user_data, const char *reason)
572 {
573         struct validate_cert_data data = { .reason = reason,
574                                                 .data = user_data,
575                                                 .processed = false };
576
577         g_cond_init(&data.cond);
578         g_mutex_init(&data.mutex);
579
580         g_mutex_lock(&data.mutex);
581
582         g_idle_add(validate_cert, &data);
583
584         while (!data.processed)
585                 g_cond_wait(&data.cond, &data.mutex);
586
587         g_mutex_unlock(&data.mutex);
588
589         g_mutex_clear(&data.mutex);
590         g_cond_clear(&data.cond);
591
592         return data.status;
593 }
594
595 struct process_form_data {
596         GMutex mutex;
597         GCond cond;
598         struct oc_auth_form *form;
599         struct oc_private_data *data;
600         bool processed;
601         int status;
602 };
603
604 static void request_input_pkcs_reply(DBusMessage *reply, void *user_data)
605 {
606         struct process_form_data *form_data = user_data;
607         struct oc_private_data *data = form_data->data;
608         struct oc_form_opt *opt;
609         const char *key;
610         const char *password = NULL;
611         DBusMessageIter iter, dict;
612
613         connman_info("provider %p", data->provider);
614
615         if (!reply) {
616                 data->err = ENOENT;
617                 goto err;
618         }
619
620         if ((data->err = vpn_agent_check_and_process_reply_error(reply,
621                                                         data->provider,
622                                                         data->task,
623                                                         data->cb,
624                                                         data->user_data))) {
625                 data->cb = NULL;
626                 data->user_data = NULL;
627                 goto err;
628         }
629
630         if (!vpn_agent_check_reply_has_dict(reply)) {
631                 data->err = ENOENT;
632                 goto err;
633         }
634
635         dbus_message_iter_init(reply, &iter);
636         dbus_message_iter_recurse(&iter, &dict);
637         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
638                 DBusMessageIter entry, value;
639
640                 dbus_message_iter_recurse(&dict, &entry);
641                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
642                         break;
643
644                 dbus_message_iter_get_basic(&entry, &key);
645
646                 if (g_str_equal(key, "OpenConnect.PKCSPassword")) {
647                         dbus_message_iter_next(&entry);
648                         if (dbus_message_iter_get_arg_type(&entry)
649                                                         != DBUS_TYPE_VARIANT)
650                                 break;
651                         dbus_message_iter_recurse(&entry, &value);
652                         if (dbus_message_iter_get_arg_type(&value)
653                                                         != DBUS_TYPE_STRING)
654                                 break;
655                         dbus_message_iter_get_basic(&value, &password);
656                         vpn_provider_set_string_hide_value(data->provider, key,
657                                                 password);
658                 }
659
660                 dbus_message_iter_next(&dict);
661         }
662
663         if (!password)
664                 goto err;
665
666         for (opt = form_data->form->opts; opt; opt = opt->next) {
667                 if (opt->flags & OC_FORM_OPT_IGNORE)
668                         continue;
669
670                 if (opt->type == OC_FORM_OPT_PASSWORD &&
671                                 g_str_has_prefix(opt->name,
672                                         "openconnect_pkcs")) {
673                         opt->_value = strdup(password);
674                         form_data->status = OC_FORM_RESULT_OK;
675                         data->tried_passphrase = true;
676                         break;
677                 }
678         }
679
680         goto out;
681
682 err:
683         form_data->status = OC_FORM_RESULT_ERR;
684
685 out:
686         form_data->processed = true;
687         g_cond_signal(&form_data->cond);
688         g_mutex_unlock(&form_data->mutex);
689 }
690
691 static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
692                                                         gpointer user_data)
693 {
694         struct oc_private_data *data;
695         const char *auth_failures[] = {
696                                 /* Cookie not valid */
697                                 "Got inappropriate HTTP CONNECT response: "
698                                                 "HTTP/1.1 401 Unauthorized",
699                                 /* Invalid cookie */
700                                 "VPN service unavailable",
701                                 NULL
702         };
703         const char *conn_failures[] = {
704                                 "Failed to connect to",
705                                 "Failed to open HTTPS connection to",
706                                 NULL
707         };
708         const char *server_key_hash = "    --servercert ";
709         char *str;
710         int err = 0;
711
712         data = user_data;
713
714         if (!data)
715                 return G_SOURCE_REMOVE;
716
717         if (source && data->err_ch != source)
718                 return G_SOURCE_REMOVE;
719
720         if ((condition & G_IO_IN)) {
721                 gsize len;
722
723                 if (g_io_channel_read_line(source, &str, &len, NULL,
724                                         NULL) != G_IO_STATUS_NORMAL)
725                         err = EIO;
726                 else
727                         g_strchomp(str);
728
729                 connman_info("openconnect: %s", str);
730
731                 if (err || !str || !*str) {
732                         connman_info("error reading from openconnect");
733                 } else if (g_str_has_prefix(str, server_key_hash)) {
734                         const char *fingerprint;
735                         int position;
736                         bool allow_self_signed;
737
738                         allow_self_signed = vpn_provider_get_boolean(
739                                         data->provider,
740                                         "OpenConnect.AllowSelfSignedCert",
741                                         false);
742
743                         if (allow_self_signed) {
744                                 position = strlen(server_key_hash) + 1;
745                                 fingerprint = g_strstrip(str + position);
746
747                                 connman_info("Set server key hash: \"%s\"",
748                                                         fingerprint);
749
750                                 vpn_provider_set_string(data->provider,
751                                                 "OpenConnect.ServerCert",
752                                                 str + strlen(server_key_hash));
753                         } else {
754                                 connman_warn("Self signed certificate is not "
755                                                         "allowed");
756                                 err = ECONNREFUSED;
757                         }
758                 } else if (strv_contains_prefix(auth_failures, str)) {
759                         connman_warn("authentication failed: %s", str);
760                         err = EACCES;
761                 } else if (strv_contains_prefix(conn_failures, str)) {
762                         connman_warn("connection failed: %s", str);
763                         err = ECONNREFUSED;
764                 }
765
766                 g_free(str);
767         } else if (condition & (G_IO_ERR | G_IO_HUP)) {
768                 connman_info("Err channel termination");
769                 close_io_channel(data, source);
770                 return G_SOURCE_REMOVE;
771         }
772
773         if (err) {
774                 switch (err) {
775                 case EACCES:
776                         clear_provider_credentials(data->provider, true);
777                         break;
778                 case ECONNREFUSED:
779                         /*
780                          * This will trigger VPN_PROVIDER_ERROR_CONNECT_FAILED
781                          * in vpn-provider.c:connect_cb().
782                          */
783                 default:
784                         break;
785                 }
786
787                 oc_connect_done(data, err);
788         }
789
790         return G_SOURCE_CONTINUE;
791 }
792
793 static gboolean process_auth_form(void *user_data)
794 {
795         struct process_form_data *form_data = user_data;
796         struct oc_private_data *data = form_data->data;
797         struct oc_form_opt *opt;
798         const char *password;
799
800         g_mutex_lock(&form_data->mutex);
801
802         DBG("");
803
804         switch (data->connect_type) {
805         case OC_CONNECT_USERPASS:
806         case OC_CONNECT_COOKIE_WITH_USERPASS:
807                 break;
808
809         case OC_CONNECT_PKCS:
810                 password = vpn_provider_get_string(data->provider,
811                                         "OpenConnect.PKCSPassword");
812
813                 for (opt = form_data->form->opts; opt; opt = opt->next) {
814                         if (opt->flags & OC_FORM_OPT_IGNORE)
815                                 continue;
816
817                         if (opt->type == OC_FORM_OPT_PASSWORD &&
818                                         g_str_has_prefix(opt->name,
819                                                         "openconnect_pkcs"))
820                                 break;
821                 }
822
823                 if (opt) {
824                         if (password && g_strcmp0(password, "-")) {
825                                 opt->_value = strdup(password);
826                                 data->tried_passphrase = true;
827                                 form_data->status = OC_FORM_RESULT_OK;
828                                 goto out;
829                         } else {
830                                 if (data->tried_passphrase) {
831                                         vpn_provider_add_error(data->provider,
832                                                VPN_PROVIDER_ERROR_AUTH_FAILED);
833                                         clear_provider_credentials(
834                                                                 data->provider,
835                                                                 true);
836                                 }
837                                 request_input_credentials_full(data,
838                                                 request_input_pkcs_reply,
839                                                 form_data);
840                                 return G_SOURCE_REMOVE;
841                         }
842                 }
843
844                 /* fall-through */
845
846         /*
847          * In case of public key, reaching here means that the
848          * passphrase previously provided was incorrect.
849          */
850         case OC_CONNECT_PUBLICKEY:
851                 data->err = -EACCES;
852                 clear_provider_credentials(data->provider, true);
853
854                 /* fall-through */
855         default:
856                 form_data->status = OC_FORM_RESULT_ERR;
857                 goto out;
858         }
859
860         /*
861          * Form values are released with free(), so always use strdup()
862          * instead of g_strdup()
863          */
864         for (opt = form_data->form->opts; opt; opt = opt->next) {
865                 if (opt->flags & OC_FORM_OPT_IGNORE)
866                         continue;
867
868                 if (opt->type == OC_FORM_OPT_TEXT &&
869                                 g_str_has_prefix(opt->name, "user")) {
870                         const char *user = vpn_provider_get_string(
871                                                 data->provider,
872                                                 "OpenConnect.Username");
873                         if (user)
874                                 opt->_value = strdup(user);
875                 } else if (opt->type == OC_FORM_OPT_PASSWORD) {
876                         const char *pass = vpn_provider_get_string(
877                                                 data->provider,
878                                                 "OpenConnect.Password");
879                         if (pass)
880                                 opt->_value = strdup(pass);
881                 }
882         }
883
884         form_data->status = OC_FORM_RESULT_OK;
885
886 out:
887         form_data->processed = true;
888         g_cond_signal(&form_data->cond);
889         g_mutex_unlock(&form_data->mutex);
890
891         return G_SOURCE_REMOVE;
892 }
893
894 static int oc_process_auth_form(void *user_data, struct oc_auth_form *form)
895 {
896         struct process_form_data data = { .form = form,
897                                                 .data = user_data,
898                                                 .processed = false };
899
900         DBG("");
901
902         g_cond_init(&data.cond);
903         g_mutex_init(&data.mutex);
904
905         g_mutex_lock(&data.mutex);
906         g_idle_add(process_auth_form, &data);
907
908         while (!data.processed)
909                 g_cond_wait(&data.cond, &data.mutex);
910
911         g_mutex_unlock(&data.mutex);
912
913         g_mutex_clear(&data.mutex);
914         g_cond_clear(&data.cond);
915
916         return data.status;
917 }
918
919 static gboolean authenticated(void *user_data)
920 {
921         struct oc_private_data *data = user_data;
922         int rv = GPOINTER_TO_INT(g_thread_join(data->cookie_thread));
923
924         DBG("");
925
926         data->cookie_thread = NULL;
927
928         if (rv == 0)
929                 rv = run_connect(data, openconnect_get_cookie(data->vpninfo));
930         else if (rv < 0)
931                 clear_provider_credentials(data->provider, true);
932
933         openconnect_vpninfo_free(data->vpninfo);
934         data->vpninfo = NULL;
935
936         if (rv != -EINPROGRESS) {
937                 oc_connect_done(data, data->err ? data->err : rv);
938                 free_private_data(data);
939         }
940
941         return G_SOURCE_REMOVE;
942 }
943
944 static void *obtain_cookie_thread(void *user_data)
945 {
946         struct oc_private_data *data = user_data;
947         int ret;
948
949         DBG("%p", data->vpninfo);
950
951         ret = openconnect_obtain_cookie(data->vpninfo);
952
953         g_idle_add(authenticated, data);
954
955         return GINT_TO_POINTER(ret);
956 }
957
958 static int authenticate(struct oc_private_data *data)
959 {
960         const char *cert = NULL;
961         const char *key = NULL;
962         const char *urlpath;
963         const char *vpnhost;
964
965         DBG("");
966
967         switch (data->connect_type) {
968         case OC_CONNECT_PKCS:
969                 cert = vpn_provider_get_string(data->provider,
970                                         "OpenConnect.PKCSClientCert");
971                 break;
972         case OC_CONNECT_PUBLICKEY:
973                 cert = vpn_provider_get_string(data->provider,
974                                         "OpenConnect.ClientCert");
975                 key = vpn_provider_get_string(data->provider,
976                                         "OpenConnect.UserPrivateKey");
977                 break;
978
979         case OC_CONNECT_USERPASS:
980         case OC_CONNECT_COOKIE_WITH_USERPASS:
981                 break;
982
983         default:
984                 return -EINVAL;
985         }
986
987         openconnect_init_ssl();
988         data->vpninfo = openconnect_vpninfo_new("ConnMan VPN Agent",
989                         oc_validate_peer_cert,
990                         NULL,
991                         oc_process_auth_form,
992                         oc_progress,
993                         data);
994
995         /* Replicating how openconnect's --usergroup argument works */
996         urlpath = vpn_provider_get_string(data->provider,
997                                                 "OpenConnect.Usergroup");
998         if (urlpath)
999                 openconnect_set_urlpath(data->vpninfo, urlpath);
1000
1001         if (vpn_provider_get_boolean(data->provider,
1002                                         "OpenConnect.DisableIPv6", false))
1003                 openconnect_disable_ipv6(data->vpninfo);
1004
1005         vpnhost = vpn_provider_get_string(data->provider,
1006                                                 "OpenConnect.VPNHost");
1007         if (!vpnhost || !*vpnhost)
1008                 vpnhost = vpn_provider_get_string(data->provider, "Host");
1009
1010         openconnect_set_hostname(data->vpninfo, vpnhost);
1011
1012         if (cert)
1013                 openconnect_set_client_cert(data->vpninfo, cert, key);
1014
1015         data->fd_cmd = openconnect_setup_cmd_pipe(data->vpninfo);
1016
1017         /*
1018          * openconnect_obtain_cookie blocks, so run it in background thread
1019          * instead
1020          */
1021         data->cookie_thread = g_thread_try_new("obtain_cookie",
1022                                                         obtain_cookie_thread,
1023                                                         data, NULL);
1024
1025         if (!data->cookie_thread)
1026                 return -EIO;
1027
1028         return -EINPROGRESS;
1029 }
1030
1031 static int run_connect(struct oc_private_data *data, const char *cookie)
1032 {
1033         struct vpn_provider *provider;
1034         struct connman_task *task;
1035         const char *vpnhost;
1036         int fd_err;
1037         int err = 0;
1038         bool allow_self_signed;
1039         const char *server_cert;
1040
1041         if (!data || !cookie)
1042                 return -EINVAL;
1043
1044         provider = data->provider;
1045         task = data->task;
1046
1047         server_cert = vpn_provider_get_string(provider,
1048                                                 "OpenConnect.ServerCert");
1049         allow_self_signed = vpn_provider_get_boolean(provider,
1050                                         "OpenConnect.AllowSelfSignedCert",
1051                                         false);
1052
1053         DBG("provider %p task %p", provider, task);
1054
1055         connman_task_add_argument(task, "--cookie-on-stdin", NULL);
1056
1057         vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
1058         if (!vpnhost || !*vpnhost)
1059                 vpnhost = vpn_provider_get_string(provider, "Host");
1060
1061         task_append_config_data(provider, task);
1062
1063         connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
1064
1065         connman_task_add_argument(task, "--interface", data->if_name);
1066
1067         connman_task_add_argument(task, (char *)vpnhost, NULL);
1068
1069         err = connman_task_run(task, oc_died, data, &data->fd_in,
1070                                 NULL, &fd_err);
1071         if (err < 0) {
1072                 err = -EIO;
1073                 goto done;
1074         }
1075
1076         if (write_data(data->fd_in, cookie) != 0) {
1077                 connman_error("openconnect failed to take cookie on "
1078                                 "stdin");
1079                 err = -EIO;
1080         }
1081
1082         if (!server_cert || !allow_self_signed) {
1083                 if (write_data(data->fd_in,
1084                                         (allow_self_signed ? "yes" : "no"))) {
1085                         connman_error("openconnect failed to take certificate "
1086                                         "acknowledgement on stdin");
1087                         err = -EIO;
1088                 }
1089         }
1090
1091         if (err) {
1092                 if (fd_err >= 0)
1093                         close(fd_err);
1094
1095                 goto done;
1096         }
1097
1098         err = -EINPROGRESS;
1099
1100         data->err_ch = g_io_channel_unix_new(fd_err);
1101
1102         /* Use ASCII encoding only */
1103         if (g_io_channel_set_encoding(data->err_ch, NULL, NULL) !=
1104                                 G_IO_STATUS_NORMAL) {
1105                 close_io_channel(data, data->err_ch);
1106                 err = -EIO;
1107         } else {
1108                 data->err_ch_id = g_io_add_watch(data->err_ch,
1109                                         G_IO_IN | G_IO_ERR | G_IO_HUP,
1110                                         (GIOFunc)io_channel_err_cb, data);
1111         }
1112
1113 done:
1114         clear_provider_credentials(data->provider, err != -EINPROGRESS);
1115
1116         return err;
1117 }
1118
1119 static void request_input_append(DBusMessageIter *iter,
1120                 const char *str_type, const char *str, void *user_data)
1121 {
1122         const char *string;
1123
1124         connman_dbus_dict_append_basic(iter, "Type",
1125                                 DBUS_TYPE_STRING, &str_type);
1126         connman_dbus_dict_append_basic(iter, "Requirement",
1127                                 DBUS_TYPE_STRING, &str);
1128
1129         if (!user_data)
1130                 return;
1131
1132         string = user_data;
1133         connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING,
1134                                 &string);
1135 }
1136
1137 static void request_input_append_informational(DBusMessageIter *iter,
1138                 void *user_data)
1139 {
1140         request_input_append(iter, "string", "informational", user_data);
1141 }
1142
1143 static void request_input_append_mandatory(DBusMessageIter *iter,
1144                 void *user_data)
1145 {
1146         request_input_append(iter, "string", "mandatory", user_data);
1147 }
1148
1149 static void request_input_append_optional(DBusMessageIter *iter,
1150                 void *user_data)
1151 {
1152         request_input_append(iter, "string", "optional", user_data);
1153 }
1154
1155 static void request_input_append_password(DBusMessageIter *iter,
1156                 void *user_data)
1157 {
1158         request_input_append(iter, "password", "mandatory", user_data);
1159 }
1160
1161 static void request_input_append_to_dict(struct vpn_provider *provider,
1162                         DBusMessageIter *dict,
1163                         connman_dbus_append_cb_t function_cb, const char *key)
1164 {
1165         const char *str;
1166         bool immutable = false;
1167
1168         if (!provider || !dict || !function_cb || !key)
1169                 return;
1170
1171         str = vpn_provider_get_string(provider, key);
1172         /* Ignore empty informational content */
1173         if (!str && function_cb == request_input_append_informational)
1174                 return;
1175
1176         /* If value is "-", it is cleared by VPN agent */
1177         if (!g_strcmp0(str, "-"))
1178                 str = NULL;
1179
1180         if (str)
1181                 immutable = vpn_provider_get_string_immutable(provider, key);
1182
1183         if (immutable) {
1184                 /* Hide immutable password types */
1185                 if (function_cb == request_input_append_password)
1186                         str = "********";
1187
1188                 /* Send immutable as informational */
1189                 function_cb = request_input_append_informational;
1190         }
1191
1192         connman_dbus_dict_append_dict(dict, key, function_cb,
1193                                 str ? (void *)str : NULL);
1194 }
1195
1196 static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
1197 {
1198         struct oc_private_data *data = user_data;
1199         const char *cookie = NULL;
1200         const char *servercert = NULL;
1201         const char *vpnhost = NULL;
1202         const char *username = NULL;
1203         const char *password = NULL;
1204         const char *pkcspassword = NULL;
1205         const char *key;
1206         DBusMessageIter iter, dict;
1207         int err;
1208
1209         connman_info("provider %p", data->provider);
1210
1211         if (!reply) {
1212                 err = ENOENT;
1213                 goto err;
1214         }
1215
1216         err = vpn_agent_check_and_process_reply_error(reply, data->provider,
1217                                 data->task, data->cb, data->user_data);
1218         if (err) {
1219                 /* Ensure cb is called only once */
1220                 data->cb = NULL;
1221                 data->user_data = NULL;
1222                 goto out;
1223         }
1224
1225         if (!vpn_agent_check_reply_has_dict(reply)) {
1226                 err = ENOENT;
1227                 goto err;
1228         }
1229
1230         dbus_message_iter_init(reply, &iter);
1231         dbus_message_iter_recurse(&iter, &dict);
1232         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
1233                 DBusMessageIter entry, value;
1234
1235                 dbus_message_iter_recurse(&dict, &entry);
1236                 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
1237                         break;
1238
1239                 dbus_message_iter_get_basic(&entry, &key);
1240
1241                 if (g_str_equal(key, "OpenConnect.Cookie")) {
1242                         dbus_message_iter_next(&entry);
1243                         if (dbus_message_iter_get_arg_type(&entry)
1244                                                         != DBUS_TYPE_VARIANT)
1245                                 break;
1246                         dbus_message_iter_recurse(&entry, &value);
1247                         if (dbus_message_iter_get_arg_type(&value)
1248                                                         != DBUS_TYPE_STRING)
1249                                 break;
1250                         dbus_message_iter_get_basic(&value, &cookie);
1251                         vpn_provider_set_string_hide_value(data->provider,
1252                                         key, cookie);
1253                 } else if (g_str_equal(key, "OpenConnect.ServerCert")) {
1254                         dbus_message_iter_next(&entry);
1255                         if (dbus_message_iter_get_arg_type(&entry)
1256                                                         != DBUS_TYPE_VARIANT)
1257                                 break;
1258                         dbus_message_iter_recurse(&entry, &value);
1259                         if (dbus_message_iter_get_arg_type(&value)
1260                                                         != DBUS_TYPE_STRING)
1261                                 break;
1262                         dbus_message_iter_get_basic(&value, &servercert);
1263                         vpn_provider_set_string(data->provider, key,
1264                                         servercert);
1265
1266                 } else if (g_str_equal(key, "OpenConnect.VPNHost")) {
1267                         dbus_message_iter_next(&entry);
1268                         if (dbus_message_iter_get_arg_type(&entry)
1269                                                         != DBUS_TYPE_VARIANT)
1270                                 break;
1271                         dbus_message_iter_recurse(&entry, &value);
1272                         if (dbus_message_iter_get_arg_type(&value)
1273                                                         != DBUS_TYPE_STRING)
1274                                 break;
1275                         dbus_message_iter_get_basic(&value, &vpnhost);
1276                         vpn_provider_set_string(data->provider, key, vpnhost);
1277                 } else if (g_str_equal(key, "Username")) {
1278                         dbus_message_iter_next(&entry);
1279                         if (dbus_message_iter_get_arg_type(&entry)
1280                                                         != DBUS_TYPE_VARIANT)
1281                                 break;
1282                         dbus_message_iter_recurse(&entry, &value);
1283                         if (dbus_message_iter_get_arg_type(&value)
1284                                                         != DBUS_TYPE_STRING)
1285                                 break;
1286                         dbus_message_iter_get_basic(&value, &username);
1287                         vpn_provider_set_string_hide_value(data->provider,
1288                                         "OpenConnect.Username", username);
1289                 } else if (g_str_equal(key, "Password")) {
1290                         dbus_message_iter_next(&entry);
1291                         if (dbus_message_iter_get_arg_type(&entry)
1292                                                         != DBUS_TYPE_VARIANT)
1293                                 break;
1294                         dbus_message_iter_recurse(&entry, &value);
1295                         if (dbus_message_iter_get_arg_type(&value)
1296                                                         != DBUS_TYPE_STRING)
1297                                 break;
1298                         dbus_message_iter_get_basic(&value, &password);
1299                         vpn_provider_set_string_hide_value(data->provider,
1300                                         "OpenConnect.Password", password);
1301                 } else if (g_str_equal(key, "OpenConnect.PKCSPassword")) {
1302                         dbus_message_iter_next(&entry);
1303                         if (dbus_message_iter_get_arg_type(&entry)
1304                                                         != DBUS_TYPE_VARIANT)
1305                                 break;
1306                         dbus_message_iter_recurse(&entry, &value);
1307                         if (dbus_message_iter_get_arg_type(&value)
1308                                                         != DBUS_TYPE_STRING)
1309                                 break;
1310                         dbus_message_iter_get_basic(&value, &pkcspassword);
1311                         vpn_provider_set_string_hide_value(data->provider, key,
1312                                                 pkcspassword);
1313                 }
1314
1315                 dbus_message_iter_next(&dict);
1316         }
1317
1318         switch (data->connect_type) {
1319         case OC_CONNECT_COOKIE:
1320                 if (!cookie) {
1321                         err = EACCES;
1322                         goto err;
1323                 }
1324
1325                 break;
1326         case OC_CONNECT_USERPASS:
1327                 /* fall through */
1328         case OC_CONNECT_COOKIE_WITH_USERPASS:
1329                 if (!username || !password) {
1330                         err = EACCES;
1331                         goto err;
1332                 }
1333
1334                 break;
1335         case OC_CONNECT_PUBLICKEY:
1336                 break; // This should not be reached.
1337         case OC_CONNECT_PKCS:
1338                 if (!pkcspassword) {
1339                         err = EACCES;
1340                         goto err;
1341                 }
1342
1343                 break;
1344         }
1345
1346         if (cookie)
1347                 err = run_connect(data, cookie);
1348         else
1349                 err = authenticate(data);
1350
1351         if (err != -EINPROGRESS)
1352                 goto err;
1353
1354         return;
1355
1356 err:
1357         oc_connect_done(data, err);
1358
1359 out:
1360         free_private_data(data);
1361 }
1362
1363 static int request_input_credentials_full(
1364                         struct oc_private_data *data,
1365                         request_input_reply_cb_t cb,
1366                         void *user_data)
1367 {
1368         DBusMessage *message;
1369         const char *path;
1370         const char *agent_sender;
1371         const char *agent_path;
1372         const char *username;
1373         DBusMessageIter iter;
1374         DBusMessageIter dict;
1375         int err;
1376         void *agent;
1377
1378         if (!data || !cb)
1379                 return -ESRCH;
1380
1381         connman_info("provider %p", data->provider);
1382
1383         agent = connman_agent_get_info(data->dbus_sender,
1384                                 &agent_sender, &agent_path);
1385         if (!data->provider || !agent || !agent_path)
1386                 return -ESRCH;
1387
1388         message = dbus_message_new_method_call(agent_sender, agent_path,
1389                                         VPN_AGENT_INTERFACE,
1390                                         "RequestInput");
1391         if (!message)
1392                 return -ENOMEM;
1393
1394         dbus_message_iter_init_append(message, &iter);
1395
1396         path = vpn_provider_get_path(data->provider);
1397         dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
1398
1399         connman_dbus_dict_open(&iter, &dict);
1400
1401         request_input_append_to_dict(data->provider, &dict,
1402                                 request_input_append_informational,
1403                                 "OpenConnect.CACert");
1404
1405         /*
1406          * For backwards compatibility add OpenConnect.ServerCert and
1407          * OpenConnect.VPNHost as mandatory only in the default authentication
1408          * mode. Otherwise. add the fields as informational. These should be
1409          * set in provider settings and not to be queried with every connection
1410          * attempt.
1411          */
1412         request_input_append_to_dict(data->provider, &dict,
1413                                 data->connect_type == OC_CONNECT_COOKIE ?
1414                                 request_input_append_optional :
1415                                 request_input_append_informational,
1416                                 "OpenConnect.ServerCert");
1417
1418         request_input_append_to_dict(data->provider, &dict,
1419                                 data->connect_type == OC_CONNECT_COOKIE ?
1420                                 request_input_append_optional :
1421                                 request_input_append_informational,
1422                                 "OpenConnect.VPNHost");
1423
1424         if (vpn_provider_get_authentication_errors(data->provider))
1425                 vpn_agent_append_auth_failure(&dict, data->provider, NULL);
1426
1427         switch (data->connect_type) {
1428         case OC_CONNECT_COOKIE:
1429                 request_input_append_to_dict(data->provider, &dict,
1430                                         request_input_append_mandatory,
1431                                         "OpenConnect.Cookie");
1432                 break;
1433         /*
1434          * The authentication is done with username and password to get the
1435          * cookie for connection.
1436          */
1437         case OC_CONNECT_COOKIE_WITH_USERPASS:
1438                 /* fallthrough */
1439         case OC_CONNECT_USERPASS:
1440                 username = vpn_provider_get_string(data->provider,
1441                                         "OpenConnect.Username");
1442                 vpn_agent_append_user_info(&dict, data->provider, username);
1443                 break;
1444         case OC_CONNECT_PUBLICKEY:
1445                 return -EINVAL;
1446         case OC_CONNECT_PKCS:
1447                 request_input_append_to_dict(data->provider, &dict,
1448                                 request_input_append_informational,
1449                                 "OpenConnect.PKCSClientCert");
1450
1451                 /* Do not allow to store or retrieve the encrypted PKCS pass */
1452                 vpn_agent_append_allow_credential_storage(&dict, false);
1453                 vpn_agent_append_allow_credential_retrieval(&dict, false);
1454
1455                 /*
1456                  * Indicate to keep credentials, the PKCS password should not
1457                  * affect the credential storing.
1458                  */
1459                 vpn_agent_append_keep_credentials(&dict, true);
1460
1461                 request_input_append_to_dict(data->provider, &dict,
1462                                         request_input_append_password,
1463                                         "OpenConnect.PKCSPassword");
1464                 break;
1465         }
1466
1467         vpn_agent_append_host_and_name(&dict, data->provider);
1468
1469         connman_dbus_dict_close(&iter, &dict);
1470
1471         err = connman_agent_queue_message(data->provider, message,
1472                         connman_timeout_input_request(), cb, user_data, agent);
1473
1474         dbus_message_unref(message);
1475
1476         if (err < 0 && err != -EBUSY) {
1477                 connman_error("cannot send agent request, error: %d", err);
1478                 return err;
1479         }
1480
1481         return -EINPROGRESS;
1482 }
1483
1484 static int request_input_credentials(struct oc_private_data *data,
1485                         request_input_reply_cb_t cb)
1486 {
1487         return request_input_credentials_full(data, cb, data);
1488 }
1489
1490 static enum oc_connect_type get_authentication_type(
1491                         struct vpn_provider *provider)
1492 {
1493         const char *auth;
1494         enum oc_connect_type type;
1495
1496         auth = vpn_provider_get_string(provider, "OpenConnect.AuthType");
1497         if (!auth)
1498                 goto out;
1499
1500         for (type = 0; connect_types[type]; type++) {
1501                 if (!g_strcmp0(auth, connect_types[type])) {
1502                         connman_info("auth type %d/%s", type,
1503                                                 connect_types[type]);
1504                         return type;
1505                 }
1506         }
1507
1508 out:
1509         /* Default to cookie */
1510         return OC_CONNECT_COOKIE;
1511 }
1512
1513 static int oc_connect(struct vpn_provider *provider,
1514                         struct connman_task *task, const char *if_name,
1515                         vpn_provider_connect_cb_t cb,
1516                         const char *dbus_sender, void *user_data)
1517 {
1518         struct oc_private_data *data;
1519         const char *vpncookie = NULL;
1520         const char *certificate;
1521         const char *username;
1522         const char *password;
1523         const char *private_key;
1524         int err;
1525
1526         connman_info("provider %p task %p", provider, task);
1527
1528         data = g_try_new0(struct oc_private_data, 1);
1529         if (!data)
1530                 return -ENOMEM;
1531
1532         vpn_provider_set_plugin_data(provider, data);
1533         data->provider = vpn_provider_ref(provider);
1534         data->task = task;
1535         data->if_name = g_strdup(if_name);
1536         data->dbus_sender = g_strdup(dbus_sender);
1537         data->cb = cb;
1538         data->user_data = user_data;
1539         data->connect_type = get_authentication_type(provider);
1540
1541         switch (data->connect_type) {
1542         case OC_CONNECT_COOKIE:
1543                 vpncookie = vpn_provider_get_string(provider,
1544                                         "OpenConnect.Cookie");
1545                 if (!vpncookie || !g_strcmp0(vpncookie, "-"))
1546                         goto request_input;
1547
1548                 break;
1549         case OC_CONNECT_USERPASS:
1550                 username = vpn_provider_get_string(provider,
1551                                         "OpenConnect.Username");
1552                 password = vpn_provider_get_string(provider,
1553                                         "OpenConnect.Password");
1554                 if (!username || !password || !g_strcmp0(username, "-") ||
1555                                         !g_strcmp0(password, "-"))
1556                         goto request_input;
1557
1558                 break;
1559         case OC_CONNECT_COOKIE_WITH_USERPASS:
1560                 vpncookie = vpn_provider_get_string(provider,
1561                                         "OpenConnect.Cookie");
1562                 /* Username and password must be set if cookie is missing */
1563                 if (!vpncookie) {
1564                         username = vpn_provider_get_string(provider,
1565                                                 "OpenConnect.Username");
1566                         password = vpn_provider_get_string(provider,
1567                                                 "OpenConnect.Password");
1568
1569                         if (!username || !password ||
1570                                                 !g_strcmp0(username, "-") ||
1571                                                 !g_strcmp0(password, "-"))
1572                                 goto request_input;
1573                 } else if (!g_strcmp0(vpncookie, "-")) {
1574                         goto request_input;
1575                 }
1576
1577                 break;
1578         case OC_CONNECT_PUBLICKEY:
1579                 certificate = vpn_provider_get_string(provider,
1580                                 "OpenConnect.ClientCert");
1581                 private_key = vpn_provider_get_string(provider,
1582                                 "OpenConnect.UserPrivateKey");
1583
1584                 if (!certificate || !private_key) {
1585                         connman_warn("missing certificate and/or private key");
1586                         oc_connect_done(data, EACCES);
1587                         free_private_data(data);
1588                         return -EACCES;
1589                 }
1590
1591                 break;
1592         case OC_CONNECT_PKCS:
1593                 certificate = vpn_provider_get_string(provider,
1594                                         "OpenConnect.PKCSClientCert");
1595                 if (!certificate) {
1596                         connman_warn("missing PKCS certificate");
1597                         oc_connect_done(data, EACCES);
1598                         free_private_data(data);
1599                         return -EACCES;
1600                 }
1601
1602                 break;
1603         }
1604
1605         if (vpncookie && g_strcmp0(vpncookie, "-"))
1606                 return run_connect(data, vpncookie);
1607         return authenticate(data);
1608
1609 request_input:
1610         err = request_input_credentials(data, request_input_credentials_reply);
1611         if (err != -EINPROGRESS) {
1612                 oc_connect_done(data, err);
1613                 vpn_provider_indicate_error(data->provider,
1614                                         VPN_PROVIDER_ERROR_LOGIN_FAILED);
1615                 free_private_data(data);
1616         }
1617
1618         return err;
1619 }
1620
1621 static void oc_disconnect(struct vpn_provider *provider)
1622 {
1623         struct oc_private_data *data;
1624
1625         connman_info("provider %p", provider);
1626
1627         if (!provider)
1628                 return;
1629
1630         /*
1631         * OpenConnect may be disconnect by timeout in connmand before running
1632         * the openconnect process. In such case it is important to cancel the
1633         * agent request to avoid having multiple ones visible.
1634         */
1635         connman_agent_cancel(provider);
1636
1637         data = vpn_provider_get_plugin_data(provider);
1638
1639         if (!data)
1640                 return;
1641
1642         if (data->cookie_thread) {
1643                 char cmd = OC_CMD_CANCEL;
1644                 int w = write(data->fd_cmd, &cmd, 1);
1645                 if (w != 1)
1646                         DBG("Write failed, might be leaking a thread");
1647         }
1648
1649 }
1650
1651 static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
1652 {
1653         const char *save_group;
1654         const char *option;
1655         int i;
1656
1657         save_group = vpn_provider_get_save_group(provider);
1658
1659         for (i = 0; i < (int)ARRAY_SIZE(oc_options); i++) {
1660                 if (strncmp(oc_options[i].cm_opt, "OpenConnect.", 12) == 0) {
1661                         option = vpn_provider_get_string(provider,
1662                                                         oc_options[i].cm_opt);
1663                         if (!option)
1664                                 continue;
1665
1666                         g_key_file_set_string(keyfile, save_group,
1667                                         oc_options[i].cm_opt, option);
1668                 }
1669         }
1670
1671         return 0;
1672 }
1673
1674 static int oc_error_code(struct vpn_provider *provider, int exit_code)
1675 {
1676         connman_info("%d", exit_code);
1677
1678         /* OpenConnect process return values are ambiguous in definition
1679          * https://github.com/openconnect/openconnect/blob/master/main.c#L1693
1680          * and it is safer not to rely on them. Login error cannot be
1681          * differentiated from connection errors, e.g., when self signed
1682          * certificate is rejected by user setting.
1683          */
1684
1685         switch (exit_code) {
1686         case 2:
1687                 /* Cookie has failed */
1688                 clear_provider_credentials(provider, false);
1689                 return VPN_PROVIDER_ERROR_LOGIN_FAILED;
1690         case 1:
1691                 /* fall through */
1692         default:
1693                 return VPN_PROVIDER_ERROR_UNKNOWN;
1694         }
1695 }
1696
1697 static int oc_route_env_parse(struct vpn_provider *provider, const char *key,
1698                 int *family, unsigned long *idx,
1699                 enum vpn_provider_route_type *type)
1700 {
1701         char *end;
1702         const char *start;
1703
1704         if (g_str_has_prefix(key, "CISCO_SPLIT_INC_")) {
1705                 *family = AF_INET;
1706                 start = key + strlen("CISCO_SPLIT_INC_");
1707         } else if (g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC_")) {
1708                 *family = AF_INET6;
1709                 start = key + strlen("CISCO_IPV6_SPLIT_INC_");
1710         } else
1711                 return -EINVAL;
1712
1713         *idx = g_ascii_strtoull(start, &end, 10);
1714
1715         if (strncmp(end, "_ADDR", 5) == 0)
1716                 *type = VPN_PROVIDER_ROUTE_TYPE_ADDR;
1717         else if (strncmp(end, "_MASK", 5) == 0)
1718                 *type = VPN_PROVIDER_ROUTE_TYPE_MASK;
1719         else if (strncmp(end, "_MASKLEN", 8) == 0 && *family == AF_INET6)
1720                 *type = VPN_PROVIDER_ROUTE_TYPE_MASK;
1721         else
1722                 return -EINVAL;
1723
1724         return 0;
1725 }
1726
1727 static struct vpn_driver vpn_driver = {
1728         .notify         = oc_notify,
1729         .connect        = oc_connect,
1730         .disconnect     = oc_disconnect,
1731         .error_code     = oc_error_code,
1732         .save           = oc_save,
1733         .route_env_parse = oc_route_env_parse,
1734 };
1735
1736 static int openconnect_init(void)
1737 {
1738         return vpn_register("openconnect", &vpn_driver, OPENCONNECT);
1739 }
1740
1741 static void openconnect_exit(void)
1742 {
1743         vpn_unregister("openconnect");
1744 }
1745
1746 CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
1747         CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)