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