5 * Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
6 * Copyright (C) 2019 Jolla Ltd. All rights reserved.
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.
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.
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
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>
45 #include <openconnect.h>
47 #include "../vpn-provider.h"
48 #include "../vpn-agent.h"
52 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
53 #define OC_MAX_READBUF_LEN 128
64 bool enabled; // Use as task parameter
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},
84 enum oc_connect_type {
85 OC_CONNECT_COOKIE = 0,
86 OC_CONNECT_COOKIE_WITH_USERPASS,
92 static const char *connect_types[] = {"cookie", "cookie_with_userpass",
93 "userpass", "publickey", "pkcs", NULL};
95 struct oc_private_data {
96 struct vpn_provider *provider;
97 struct connman_task *task;
100 vpn_provider_connect_cb_t cb;
103 GThread *cookie_thread;
104 struct openconnect_info *vpninfo;
111 enum oc_connect_type connect_type;
112 bool tried_passphrase;
115 typedef void (*request_input_reply_cb_t) (DBusMessage *reply,
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,
124 static bool is_valid_protocol(const char* protocol)
128 struct oc_vpn_proto *protos;
130 if (!protocol || !*protocol)
133 num_protocols = openconnect_get_supported_protocols(&protos);
135 for (i = 0; i < num_protocols; i++)
136 if (!strcmp(protos[i].name, protocol))
139 openconnect_free_supported_protocols(protos);
141 return i < num_protocols;
144 static void oc_connect_done(struct oc_private_data *data, int err)
146 connman_info("data %p err %d/%s", data, err, strerror(err));
148 if (data && data->cb) {
149 vpn_provider_connect_cb_t cb = data->cb;
150 void *user_data = data->user_data;
152 /* Make sure we don't invoke this callback twice */
154 data->user_data = NULL;
155 cb(data->provider, user_data, err);
159 static void close_io_channel(struct oc_private_data *data, GIOChannel *channel)
163 connman_info("data %p channel %p", data, channel);
165 if (!data || !channel)
168 if (data->err_ch == channel) {
169 id = data->err_ch_id;
179 g_io_channel_shutdown(channel, FALSE, NULL);
180 g_io_channel_unref(channel);
183 static void free_private_data(struct oc_private_data *data)
185 connman_info("data %p", data);
187 if (!data || !data->provider)
190 connman_info("provider %p", data->provider);
193 openconnect_vpninfo_free(data->vpninfo);
195 if (vpn_provider_get_plugin_data(data->provider) == data)
196 vpn_provider_set_plugin_data(data->provider, NULL);
198 vpn_provider_unref(data->provider);
203 close_io_channel(data, data->err_ch);
205 g_free(data->dbus_sender);
206 g_free(data->if_name);
210 static int task_append_config_data(struct vpn_provider *provider,
211 struct connman_task *task)
213 const char *option = NULL;
216 for (i = 0; i < (int)ARRAY_SIZE(oc_options); i++) {
217 if (!oc_options[i].oc_opt || !oc_options[i].enabled)
220 if (oc_options[i].has_value) {
221 option = vpn_provider_get_string(provider,
222 oc_options[i].cm_opt);
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,
233 /* No option is set for boolean type values. */
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))
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.
251 if (!g_strcmp0(oc_options[i].cm_opt,
252 "OpenConnect.ServerCert")) {
253 if (!vpn_provider_get_boolean(provider,
254 "OpenConnect.AllowSelfSignedCert",
259 if (connman_task_add_argument(task,
260 oc_options[i].oc_opt,
261 oc_options[i].has_value ? option : NULL) < 0)
268 static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
270 DBusMessageIter iter, dict;
271 const char *reason, *key, *value;
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;
279 connman_info("provider %p", provider);
281 data = vpn_provider_get_plugin_data(provider);
283 dbus_message_iter_init(msg, &iter);
285 dbus_message_iter_get_basic(&iter, &reason);
286 dbus_message_iter_next(&iter);
289 connman_error("No provider found");
290 oc_connect_done(data, ENOENT);
291 return VPN_STATE_FAILURE;
294 if (strcmp(reason, "connect"))
295 return VPN_STATE_DISCONNECT;
297 domain = g_strdup(vpn_provider_get_string(provider, "VPN.Domain"));
299 dbus_message_iter_recurse(&iter, &dict);
301 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
302 DBusMessageIter entry;
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);
309 if (strcmp(key, "CISCO_CSTP_OPTIONS"))
310 DBG("%s = %s", key, value);
312 if (!strcmp(key, "VPNGATEWAY"))
313 gateway = g_strdup(value);
315 if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
316 addressv4 = g_strdup(value);
318 if (!strcmp(key, "INTERNAL_IP6_ADDRESS")) {
319 addressv6 = g_strdup(value);
323 if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
324 netmask = g_strdup(value);
326 if (!strcmp(key, "INTERNAL_IP6_NETMASK")) {
329 /* The netmask contains the address and the prefix */
330 sep = strchr(value, '/');
332 unsigned char ip_len = sep - value;
334 addressv6 = g_strndup(value, ip_len);
335 prefix_len = (unsigned char)
336 strtol(sep + 1, NULL, 10);
340 if (!strcmp(key, "INTERNAL_IP4_DNS") ||
341 !strcmp(key, "INTERNAL_IP6_DNS"))
342 vpn_provider_set_nameservers(provider, value);
344 if (!strcmp(key, "CISCO_PROXY_PAC"))
345 vpn_provider_set_pac(provider, value);
347 if (!domain && !strcmp(key, "CISCO_DEF_DOMAIN")) {
349 domain = g_strdup(value);
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);
356 dbus_message_iter_next(&dict);
359 DBG("%p %p", addressv4, addressv6);
362 ipaddress = connman_ipaddress_alloc(AF_INET);
364 ipaddress = connman_ipaddress_alloc(AF_INET6);
375 return VPN_STATE_FAILURE;
379 connman_ipaddress_set_ipv4(ipaddress, addressv4,
382 connman_ipaddress_set_ipv6(ipaddress, addressv6,
383 prefix_len, gateway);
385 connman_ipaddress_set_p2p(ipaddress, true);
386 vpn_provider_set_ipaddress(provider, ipaddress);
387 vpn_provider_set_domain(provider, domain);
394 connman_ipaddress_free(ipaddress);
396 oc_connect_done(data, 0);
397 return VPN_STATE_CONNECT;
400 static ssize_t full_write(int fd, const char *buf, size_t 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));
418 static ssize_t write_data(int fd, const char *data)
426 buf = g_strdup_printf("%s\n", data);
428 len = full_write(fd, buf, strlen(buf));
435 static void oc_died(struct connman_task *task, int exit_code, void *user_data)
437 struct oc_private_data *data = user_data;
439 connman_info("task %p data %p exit_code %d user_data %p", task, data,
440 exit_code, user_data);
445 if (data->provider) {
446 connman_agent_cancel(data->provider);
449 vpn_died(task, exit_code, data->provider);
452 free_private_data(data);
455 static bool strv_contains_prefix(const char *strv[], const char *str)
459 if (!strv || !str || !*str)
462 for (i = 0; strv[i]; i++) {
463 if (g_str_has_prefix(str, strv[i]))
470 static void clear_provider_credentials(struct vpn_provider *provider,
471 bool clear_pkcs_pass)
473 const char *keys[] = { "OpenConnect.PKCSPassword",
474 "OpenConnect.Username",
475 "OpenConnect.Password",
476 "OpenConnect.Cookie",
481 connman_info("provider %p", provider);
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],
490 static void __attribute__ ((format(printf, 3, 4))) oc_progress(void *user_data,
491 int level, const char *fmt, ...)
497 msg = g_strdup_vprintf(fmt, ap);
499 connman_debug("%s", msg);
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.
509 enum oc_cert_status {
514 struct validate_cert_data {
518 struct oc_private_data *data;
520 enum oc_cert_status status;
523 static gboolean validate_cert(void *user_data)
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;
532 g_mutex_lock(&cert_data->mutex);
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",
541 if (!allow_self_signed) {
542 cert_data->status = OC_CERT_REJECT;
543 } else if (server_cert) {
545 * Check peer cert hash may return negative values on errors,
546 * but anything non-zero is acceptable.
548 cert_data->status = openconnect_check_peer_cert_hash(
553 * We could verify this from the agent at this point, and
554 * release the thread upon reply.
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;
564 cert_data->processed = true;
565 g_cond_signal(&cert_data->cond);
566 g_mutex_unlock(&cert_data->mutex);
568 return G_SOURCE_REMOVE;
571 static int oc_validate_peer_cert(void *user_data, const char *reason)
573 struct validate_cert_data data = { .reason = reason,
575 .processed = false };
577 g_cond_init(&data.cond);
578 g_mutex_init(&data.mutex);
580 g_mutex_lock(&data.mutex);
582 g_idle_add(validate_cert, &data);
584 while (!data.processed)
585 g_cond_wait(&data.cond, &data.mutex);
587 g_mutex_unlock(&data.mutex);
589 g_mutex_clear(&data.mutex);
590 g_cond_clear(&data.cond);
595 struct process_form_data {
598 struct oc_auth_form *form;
599 struct oc_private_data *data;
604 static void request_input_pkcs_reply(DBusMessage *reply, void *user_data)
606 struct process_form_data *form_data = user_data;
607 struct oc_private_data *data = form_data->data;
608 struct oc_form_opt *opt;
610 const char *password = NULL;
611 DBusMessageIter iter, dict;
613 connman_info("provider %p", data->provider);
620 if ((data->err = vpn_agent_check_and_process_reply_error(reply,
626 data->user_data = NULL;
630 if (!vpn_agent_check_reply_has_dict(reply)) {
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;
640 dbus_message_iter_recurse(&dict, &entry);
641 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
644 dbus_message_iter_get_basic(&entry, &key);
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)
651 dbus_message_iter_recurse(&entry, &value);
652 if (dbus_message_iter_get_arg_type(&value)
655 dbus_message_iter_get_basic(&value, &password);
656 vpn_provider_set_string_hide_value(data->provider, key,
660 dbus_message_iter_next(&dict);
666 for (opt = form_data->form->opts; opt; opt = opt->next) {
667 if (opt->flags & OC_FORM_OPT_IGNORE)
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;
683 form_data->status = OC_FORM_RESULT_ERR;
686 form_data->processed = true;
687 g_cond_signal(&form_data->cond);
688 g_mutex_unlock(&form_data->mutex);
691 static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
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",
700 "VPN service unavailable",
703 const char *conn_failures[] = {
704 "Failed to connect to",
705 "Failed to open HTTPS connection to",
708 const char *server_key_hash = " --servercert ";
715 return G_SOURCE_REMOVE;
717 if (source && data->err_ch != source)
718 return G_SOURCE_REMOVE;
720 if ((condition & G_IO_IN)) {
723 if (g_io_channel_read_line(source, &str, &len, NULL,
724 NULL) != G_IO_STATUS_NORMAL)
729 connman_info("openconnect: %s", str);
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;
736 bool allow_self_signed;
738 allow_self_signed = vpn_provider_get_boolean(
740 "OpenConnect.AllowSelfSignedCert",
743 if (allow_self_signed) {
744 position = strlen(server_key_hash) + 1;
745 fingerprint = g_strstrip(str + position);
747 connman_info("Set server key hash: \"%s\"",
750 vpn_provider_set_string(data->provider,
751 "OpenConnect.ServerCert",
752 str + strlen(server_key_hash));
754 connman_warn("Self signed certificate is not "
758 } else if (strv_contains_prefix(auth_failures, str)) {
759 connman_warn("authentication failed: %s", str);
761 } else if (strv_contains_prefix(conn_failures, str)) {
762 connman_warn("connection failed: %s", 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;
776 clear_provider_credentials(data->provider, true);
780 * This will trigger VPN_PROVIDER_ERROR_CONNECT_FAILED
781 * in vpn-provider.c:connect_cb().
787 oc_connect_done(data, err);
790 return G_SOURCE_CONTINUE;
793 static gboolean process_auth_form(void *user_data)
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;
800 g_mutex_lock(&form_data->mutex);
804 switch (data->connect_type) {
805 case OC_CONNECT_USERPASS:
806 case OC_CONNECT_COOKIE_WITH_USERPASS:
809 case OC_CONNECT_PKCS:
810 password = vpn_provider_get_string(data->provider,
811 "OpenConnect.PKCSPassword");
813 for (opt = form_data->form->opts; opt; opt = opt->next) {
814 if (opt->flags & OC_FORM_OPT_IGNORE)
817 if (opt->type == OC_FORM_OPT_PASSWORD &&
818 g_str_has_prefix(opt->name,
824 if (password && g_strcmp0(password, "-")) {
825 opt->_value = strdup(password);
826 data->tried_passphrase = true;
827 form_data->status = OC_FORM_RESULT_OK;
830 if (data->tried_passphrase) {
831 vpn_provider_add_error(data->provider,
832 VPN_PROVIDER_ERROR_AUTH_FAILED);
833 clear_provider_credentials(
837 request_input_credentials_full(data,
838 request_input_pkcs_reply,
840 return G_SOURCE_REMOVE;
847 * In case of public key, reaching here means that the
848 * passphrase previously provided was incorrect.
850 case OC_CONNECT_PUBLICKEY:
852 clear_provider_credentials(data->provider, true);
856 form_data->status = OC_FORM_RESULT_ERR;
861 * Form values are released with free(), so always use strdup()
862 * instead of g_strdup()
864 for (opt = form_data->form->opts; opt; opt = opt->next) {
865 if (opt->flags & OC_FORM_OPT_IGNORE)
868 if (opt->type == OC_FORM_OPT_TEXT &&
869 g_str_has_prefix(opt->name, "user")) {
870 const char *user = vpn_provider_get_string(
872 "OpenConnect.Username");
874 opt->_value = strdup(user);
875 } else if (opt->type == OC_FORM_OPT_PASSWORD) {
876 const char *pass = vpn_provider_get_string(
878 "OpenConnect.Password");
880 opt->_value = strdup(pass);
884 form_data->status = OC_FORM_RESULT_OK;
887 form_data->processed = true;
888 g_cond_signal(&form_data->cond);
889 g_mutex_unlock(&form_data->mutex);
891 return G_SOURCE_REMOVE;
894 static int oc_process_auth_form(void *user_data, struct oc_auth_form *form)
896 struct process_form_data data = { .form = form,
898 .processed = false };
902 g_cond_init(&data.cond);
903 g_mutex_init(&data.mutex);
905 g_mutex_lock(&data.mutex);
906 g_idle_add(process_auth_form, &data);
908 while (!data.processed)
909 g_cond_wait(&data.cond, &data.mutex);
911 g_mutex_unlock(&data.mutex);
913 g_mutex_clear(&data.mutex);
914 g_cond_clear(&data.cond);
919 static gboolean authenticated(void *user_data)
921 struct oc_private_data *data = user_data;
922 int rv = GPOINTER_TO_INT(g_thread_join(data->cookie_thread));
926 data->cookie_thread = NULL;
929 rv = run_connect(data, openconnect_get_cookie(data->vpninfo));
931 clear_provider_credentials(data->provider, true);
933 openconnect_vpninfo_free(data->vpninfo);
934 data->vpninfo = NULL;
936 if (rv != -EINPROGRESS) {
937 oc_connect_done(data, data->err ? data->err : rv);
938 free_private_data(data);
941 return G_SOURCE_REMOVE;
944 static void *obtain_cookie_thread(void *user_data)
946 struct oc_private_data *data = user_data;
949 DBG("%p", data->vpninfo);
951 ret = openconnect_obtain_cookie(data->vpninfo);
953 g_idle_add(authenticated, data);
955 return GINT_TO_POINTER(ret);
958 static int authenticate(struct oc_private_data *data)
960 const char *cert = NULL;
961 const char *key = NULL;
967 switch (data->connect_type) {
968 case OC_CONNECT_PKCS:
969 cert = vpn_provider_get_string(data->provider,
970 "OpenConnect.PKCSClientCert");
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");
979 case OC_CONNECT_USERPASS:
980 case OC_CONNECT_COOKIE_WITH_USERPASS:
987 openconnect_init_ssl();
988 data->vpninfo = openconnect_vpninfo_new("ConnMan VPN Agent",
989 oc_validate_peer_cert,
991 oc_process_auth_form,
995 /* Replicating how openconnect's --usergroup argument works */
996 urlpath = vpn_provider_get_string(data->provider,
997 "OpenConnect.Usergroup");
999 openconnect_set_urlpath(data->vpninfo, urlpath);
1001 if (vpn_provider_get_boolean(data->provider,
1002 "OpenConnect.DisableIPv6", false))
1003 openconnect_disable_ipv6(data->vpninfo);
1005 vpnhost = vpn_provider_get_string(data->provider,
1006 "OpenConnect.VPNHost");
1007 if (!vpnhost || !*vpnhost)
1008 vpnhost = vpn_provider_get_string(data->provider, "Host");
1010 openconnect_set_hostname(data->vpninfo, vpnhost);
1013 openconnect_set_client_cert(data->vpninfo, cert, key);
1015 data->fd_cmd = openconnect_setup_cmd_pipe(data->vpninfo);
1018 * openconnect_obtain_cookie blocks, so run it in background thread
1021 data->cookie_thread = g_thread_try_new("obtain_cookie",
1022 obtain_cookie_thread,
1025 if (!data->cookie_thread)
1028 return -EINPROGRESS;
1031 static int run_connect(struct oc_private_data *data, const char *cookie)
1033 struct vpn_provider *provider;
1034 struct connman_task *task;
1035 const char *vpnhost;
1038 bool allow_self_signed;
1039 const char *server_cert;
1041 if (!data || !cookie)
1044 provider = data->provider;
1047 server_cert = vpn_provider_get_string(provider,
1048 "OpenConnect.ServerCert");
1049 allow_self_signed = vpn_provider_get_boolean(provider,
1050 "OpenConnect.AllowSelfSignedCert",
1053 DBG("provider %p task %p", provider, task);
1055 connman_task_add_argument(task, "--cookie-on-stdin", NULL);
1057 vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
1058 if (!vpnhost || !*vpnhost)
1059 vpnhost = vpn_provider_get_string(provider, "Host");
1061 task_append_config_data(provider, task);
1063 connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
1065 connman_task_add_argument(task, "--interface", data->if_name);
1067 connman_task_add_argument(task, (char *)vpnhost, NULL);
1069 err = connman_task_run(task, oc_died, data, &data->fd_in,
1076 if (write_data(data->fd_in, cookie) != 0) {
1077 connman_error("openconnect failed to take cookie on "
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");
1100 data->err_ch = g_io_channel_unix_new(fd_err);
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);
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);
1114 clear_provider_credentials(data->provider, err != -EINPROGRESS);
1119 static void request_input_append(DBusMessageIter *iter,
1120 const char *str_type, const char *str, void *user_data)
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);
1133 connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING,
1137 static void request_input_append_informational(DBusMessageIter *iter,
1140 request_input_append(iter, "string", "informational", user_data);
1143 static void request_input_append_mandatory(DBusMessageIter *iter,
1146 request_input_append(iter, "string", "mandatory", user_data);
1149 static void request_input_append_optional(DBusMessageIter *iter,
1152 request_input_append(iter, "string", "optional", user_data);
1155 static void request_input_append_password(DBusMessageIter *iter,
1158 request_input_append(iter, "password", "mandatory", user_data);
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)
1166 bool immutable = false;
1168 if (!provider || !dict || !function_cb || !key)
1171 str = vpn_provider_get_string(provider, key);
1172 /* Ignore empty informational content */
1173 if (!str && function_cb == request_input_append_informational)
1176 /* If value is "-", it is cleared by VPN agent */
1177 if (!g_strcmp0(str, "-"))
1181 immutable = vpn_provider_get_string_immutable(provider, key);
1184 /* Hide immutable password types */
1185 if (function_cb == request_input_append_password)
1188 /* Send immutable as informational */
1189 function_cb = request_input_append_informational;
1192 connman_dbus_dict_append_dict(dict, key, function_cb,
1193 str ? (void *)str : NULL);
1196 static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
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;
1206 DBusMessageIter iter, dict;
1209 connman_info("provider %p", data->provider);
1216 err = vpn_agent_check_and_process_reply_error(reply, data->provider,
1217 data->task, data->cb, data->user_data);
1219 /* Ensure cb is called only once */
1221 data->user_data = NULL;
1225 if (!vpn_agent_check_reply_has_dict(reply)) {
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;
1235 dbus_message_iter_recurse(&dict, &entry);
1236 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
1239 dbus_message_iter_get_basic(&entry, &key);
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)
1246 dbus_message_iter_recurse(&entry, &value);
1247 if (dbus_message_iter_get_arg_type(&value)
1248 != DBUS_TYPE_STRING)
1250 dbus_message_iter_get_basic(&value, &cookie);
1251 vpn_provider_set_string_hide_value(data->provider,
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)
1258 dbus_message_iter_recurse(&entry, &value);
1259 if (dbus_message_iter_get_arg_type(&value)
1260 != DBUS_TYPE_STRING)
1262 dbus_message_iter_get_basic(&value, &servercert);
1263 vpn_provider_set_string(data->provider, key,
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)
1271 dbus_message_iter_recurse(&entry, &value);
1272 if (dbus_message_iter_get_arg_type(&value)
1273 != DBUS_TYPE_STRING)
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)
1282 dbus_message_iter_recurse(&entry, &value);
1283 if (dbus_message_iter_get_arg_type(&value)
1284 != DBUS_TYPE_STRING)
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)
1294 dbus_message_iter_recurse(&entry, &value);
1295 if (dbus_message_iter_get_arg_type(&value)
1296 != DBUS_TYPE_STRING)
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)
1306 dbus_message_iter_recurse(&entry, &value);
1307 if (dbus_message_iter_get_arg_type(&value)
1308 != DBUS_TYPE_STRING)
1310 dbus_message_iter_get_basic(&value, &pkcspassword);
1311 vpn_provider_set_string_hide_value(data->provider, key,
1315 dbus_message_iter_next(&dict);
1318 switch (data->connect_type) {
1319 case OC_CONNECT_COOKIE:
1326 case OC_CONNECT_USERPASS:
1328 case OC_CONNECT_COOKIE_WITH_USERPASS:
1329 if (!username || !password) {
1335 case OC_CONNECT_PUBLICKEY:
1336 break; // This should not be reached.
1337 case OC_CONNECT_PKCS:
1338 if (!pkcspassword) {
1347 err = run_connect(data, cookie);
1349 err = authenticate(data);
1351 if (err != -EINPROGRESS)
1357 oc_connect_done(data, err);
1360 free_private_data(data);
1363 static int request_input_credentials_full(
1364 struct oc_private_data *data,
1365 request_input_reply_cb_t cb,
1368 DBusMessage *message;
1370 const char *agent_sender;
1371 const char *agent_path;
1372 const char *username;
1373 DBusMessageIter iter;
1374 DBusMessageIter dict;
1381 connman_info("provider %p", data->provider);
1383 agent = connman_agent_get_info(data->dbus_sender,
1384 &agent_sender, &agent_path);
1385 if (!data->provider || !agent || !agent_path)
1388 message = dbus_message_new_method_call(agent_sender, agent_path,
1389 VPN_AGENT_INTERFACE,
1394 dbus_message_iter_init_append(message, &iter);
1396 path = vpn_provider_get_path(data->provider);
1397 dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
1399 connman_dbus_dict_open(&iter, &dict);
1401 request_input_append_to_dict(data->provider, &dict,
1402 request_input_append_informational,
1403 "OpenConnect.CACert");
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
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");
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");
1424 if (vpn_provider_get_authentication_errors(data->provider))
1425 vpn_agent_append_auth_failure(&dict, data->provider, NULL);
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");
1434 * The authentication is done with username and password to get the
1435 * cookie for connection.
1437 case OC_CONNECT_COOKIE_WITH_USERPASS:
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);
1444 case OC_CONNECT_PUBLICKEY:
1446 case OC_CONNECT_PKCS:
1447 request_input_append_to_dict(data->provider, &dict,
1448 request_input_append_informational,
1449 "OpenConnect.PKCSClientCert");
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);
1456 * Indicate to keep credentials, the PKCS password should not
1457 * affect the credential storing.
1459 vpn_agent_append_keep_credentials(&dict, true);
1461 request_input_append_to_dict(data->provider, &dict,
1462 request_input_append_password,
1463 "OpenConnect.PKCSPassword");
1467 vpn_agent_append_host_and_name(&dict, data->provider);
1469 connman_dbus_dict_close(&iter, &dict);
1471 err = connman_agent_queue_message(data->provider, message,
1472 connman_timeout_input_request(), cb, user_data, agent);
1474 dbus_message_unref(message);
1476 if (err < 0 && err != -EBUSY) {
1477 connman_error("cannot send agent request, error: %d", err);
1481 return -EINPROGRESS;
1484 static int request_input_credentials(struct oc_private_data *data,
1485 request_input_reply_cb_t cb)
1487 return request_input_credentials_full(data, cb, data);
1490 static enum oc_connect_type get_authentication_type(
1491 struct vpn_provider *provider)
1494 enum oc_connect_type type;
1496 auth = vpn_provider_get_string(provider, "OpenConnect.AuthType");
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]);
1509 /* Default to cookie */
1510 return OC_CONNECT_COOKIE;
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)
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;
1526 connman_info("provider %p task %p", provider, task);
1528 data = g_try_new0(struct oc_private_data, 1);
1532 vpn_provider_set_plugin_data(provider, data);
1533 data->provider = vpn_provider_ref(provider);
1535 data->if_name = g_strdup(if_name);
1536 data->dbus_sender = g_strdup(dbus_sender);
1538 data->user_data = user_data;
1539 data->connect_type = get_authentication_type(provider);
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, "-"))
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, "-"))
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 */
1564 username = vpn_provider_get_string(provider,
1565 "OpenConnect.Username");
1566 password = vpn_provider_get_string(provider,
1567 "OpenConnect.Password");
1569 if (!username || !password ||
1570 !g_strcmp0(username, "-") ||
1571 !g_strcmp0(password, "-"))
1573 } else if (!g_strcmp0(vpncookie, "-")) {
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");
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);
1592 case OC_CONNECT_PKCS:
1593 certificate = vpn_provider_get_string(provider,
1594 "OpenConnect.PKCSClientCert");
1596 connman_warn("missing PKCS certificate");
1597 oc_connect_done(data, EACCES);
1598 free_private_data(data);
1605 if (vpncookie && g_strcmp0(vpncookie, "-"))
1606 return run_connect(data, vpncookie);
1607 return authenticate(data);
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);
1621 static void oc_disconnect(struct vpn_provider *provider)
1623 struct oc_private_data *data;
1625 connman_info("provider %p", provider);
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.
1635 connman_agent_cancel(provider);
1637 data = vpn_provider_get_plugin_data(provider);
1642 if (data->cookie_thread) {
1643 char cmd = OC_CMD_CANCEL;
1644 int w = write(data->fd_cmd, &cmd, 1);
1646 DBG("Write failed, might be leaking a thread");
1651 static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
1653 const char *save_group;
1657 save_group = vpn_provider_get_save_group(provider);
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);
1666 g_key_file_set_string(keyfile, save_group,
1667 oc_options[i].cm_opt, option);
1674 static int oc_error_code(struct vpn_provider *provider, int exit_code)
1676 connman_info("%d", exit_code);
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.
1685 switch (exit_code) {
1687 /* Cookie has failed */
1688 clear_provider_credentials(provider, false);
1689 return VPN_PROVIDER_ERROR_LOGIN_FAILED;
1693 return VPN_PROVIDER_ERROR_UNKNOWN;
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)
1704 if (g_str_has_prefix(key, "CISCO_SPLIT_INC_")) {
1706 start = key + strlen("CISCO_SPLIT_INC_");
1707 } else if (g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC_")) {
1709 start = key + strlen("CISCO_IPV6_SPLIT_INC_");
1713 *idx = g_ascii_strtoull(start, &end, 10);
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;
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,
1733 .route_env_parse = oc_route_env_parse,
1736 static int openconnect_init(void)
1738 return vpn_register("openconnect", &vpn_driver, OPENCONNECT);
1741 static void openconnect_exit(void)
1743 vpn_unregister("openconnect");
1746 CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
1747 CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)