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 "../vpn-provider.h"
46 #include "../vpn-agent.h"
50 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
51 #define OC_MAX_READBUF_LEN 128
62 bool enabled; // Use as task parameter
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},
82 enum oc_connect_type {
83 OC_CONNECT_COOKIE = 0,
84 OC_CONNECT_COOKIE_WITH_USERPASS,
90 static const char *connect_types[] = {"cookie", "cookie_with_userpass",
91 "userpass", "publickey", "pkcs", NULL};
92 static const char *protocols[] = { "anyconnect", "nc", "gp", NULL};
94 struct oc_private_data {
95 struct vpn_provider *provider;
96 struct connman_task *task;
99 vpn_provider_connect_cb_t cb;
106 enum oc_connect_type connect_type;
110 static bool is_valid_protocol(const char* protocol)
112 if (!protocol || !*protocol)
115 return g_strv_contains(protocols, protocol);
118 static void oc_connect_done(struct oc_private_data *data, int err)
120 connman_info("data %p err %d/%s", data, err, strerror(err));
122 if (data && data->cb) {
123 vpn_provider_connect_cb_t cb = data->cb;
124 void *user_data = data->user_data;
126 /* Make sure we don't invoke this callback twice */
128 data->user_data = NULL;
129 cb(data->provider, user_data, err);
133 static void close_io_channel(struct oc_private_data *data, GIOChannel *channel)
137 connman_info("data %p channel %p", data, channel);
139 if (!data || !channel)
142 if (data->out_ch == channel) {
143 id = data->out_ch_id;
146 } else if (data->err_ch == channel) {
147 id = data->err_ch_id;
157 g_io_channel_shutdown(channel, FALSE, NULL);
158 g_io_channel_unref(channel);
161 static void free_private_data(struct oc_private_data *data)
163 connman_info("data %p", data);
165 if (!data || !data->provider)
168 connman_info("provider %p", data->provider);
170 if (vpn_provider_get_plugin_data(data->provider) == data)
171 vpn_provider_set_plugin_data(data->provider, NULL);
173 vpn_provider_unref(data->provider);
178 close_io_channel(data, data->out_ch);
179 close_io_channel(data, data->err_ch);
181 g_free(data->dbus_sender);
182 g_free(data->if_name);
186 static int task_append_config_data(struct vpn_provider *provider,
187 struct connman_task *task)
189 const char *option = NULL;
192 for (i = 0; i < (int)ARRAY_SIZE(oc_options); i++) {
193 if (!oc_options[i].oc_opt || !oc_options[i].enabled)
196 if (oc_options[i].has_value) {
197 option = vpn_provider_get_string(provider,
198 oc_options[i].cm_opt);
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,
209 /* No option is set for boolean type values. */
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))
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.
227 if (!g_strcmp0(oc_options[i].cm_opt,
228 "OpenConnect.ServerCert")) {
229 if (!vpn_provider_get_boolean(provider,
230 "OpenConnect.AllowSelfSignedCert",
235 if (connman_task_add_argument(task,
236 oc_options[i].oc_opt,
237 oc_options[i].has_value ? option : NULL) < 0)
244 static int oc_notify(DBusMessage *msg, struct vpn_provider *provider)
246 DBusMessageIter iter, dict;
247 const char *reason, *key, *value;
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;
255 connman_info("provider %p", provider);
257 data = vpn_provider_get_plugin_data(provider);
259 dbus_message_iter_init(msg, &iter);
261 dbus_message_iter_get_basic(&iter, &reason);
262 dbus_message_iter_next(&iter);
265 connman_error("No provider found");
266 oc_connect_done(data, ENOENT);
267 return VPN_STATE_FAILURE;
270 if (strcmp(reason, "connect"))
271 return VPN_STATE_DISCONNECT;
273 domain = g_strdup(vpn_provider_get_string(provider, "VPN.Domain"));
275 dbus_message_iter_recurse(&iter, &dict);
277 while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
278 DBusMessageIter entry;
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);
285 if (strcmp(key, "CISCO_CSTP_OPTIONS"))
286 DBG("%s = %s", key, value);
288 if (!strcmp(key, "VPNGATEWAY"))
289 gateway = g_strdup(value);
291 if (!strcmp(key, "INTERNAL_IP4_ADDRESS"))
292 addressv4 = g_strdup(value);
294 if (!strcmp(key, "INTERNAL_IP6_ADDRESS")) {
295 addressv6 = g_strdup(value);
299 if (!strcmp(key, "INTERNAL_IP4_NETMASK"))
300 netmask = g_strdup(value);
302 if (!strcmp(key, "INTERNAL_IP6_NETMASK")) {
305 /* The netmask contains the address and the prefix */
306 sep = strchr(value, '/');
308 unsigned char ip_len = sep - value;
310 addressv6 = g_strndup(value, ip_len);
311 prefix_len = (unsigned char)
312 strtol(sep + 1, NULL, 10);
316 if (!strcmp(key, "INTERNAL_IP4_DNS") ||
317 !strcmp(key, "INTERNAL_IP6_DNS"))
318 vpn_provider_set_nameservers(provider, value);
320 if (!strcmp(key, "CISCO_PROXY_PAC"))
321 vpn_provider_set_pac(provider, value);
323 if (!domain && !strcmp(key, "CISCO_DEF_DOMAIN")) {
325 domain = g_strdup(value);
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);
332 dbus_message_iter_next(&dict);
335 DBG("%p %p", addressv4, addressv6);
338 ipaddress = connman_ipaddress_alloc(AF_INET);
340 ipaddress = connman_ipaddress_alloc(AF_INET6);
351 return VPN_STATE_FAILURE;
355 connman_ipaddress_set_ipv4(ipaddress, addressv4,
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);
368 connman_ipaddress_free(ipaddress);
370 oc_connect_done(data, 0);
371 return VPN_STATE_CONNECT;
374 static ssize_t full_write(int fd, const void *buf, size_t 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));
392 static ssize_t write_data(int fd, const char *data)
400 buf = g_strdup_printf("%s\n", data);
402 len = full_write(fd, buf, strlen(buf));
409 static void oc_died(struct connman_task *task, int exit_code, void *user_data)
411 struct oc_private_data *data = user_data;
413 connman_info("task %p data %p exit_code %d user_data %p", task, data,
414 exit_code, user_data);
419 if (data->provider) {
420 connman_agent_cancel(data->provider);
423 vpn_died(task, exit_code, data->provider);
426 free_private_data(data);
429 static gboolean io_channel_out_cb(GIOChannel *source, GIOCondition condition,
432 struct oc_private_data *data;
437 if (data->out_ch != source)
438 return G_SOURCE_REMOVE;
440 if ((condition & G_IO_IN) &&
441 g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
442 G_IO_STATUS_NORMAL) {
446 /* Only cookie is printed to stdout */
447 vpn_provider_set_string_hide_value(data->provider,
448 "OpenConnect.Cookie", 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;
457 return G_SOURCE_CONTINUE;
460 static bool strv_contains_prefix(const char *strv[], const char *str)
464 if (!strv || !str || !*str)
467 for (i = 0; strv[i]; i++) {
468 if (g_str_has_prefix(str, strv[i]))
475 static void clear_provider_credentials(struct vpn_provider *provider)
477 const char *keys[] = { "OpenConnect.Username",
478 "OpenConnect.Password",
479 "OpenConnect.PKCSPassword",
480 "OpenConnect.Cookie",
485 connman_info("provider %p", provider);
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],
494 typedef void (* request_input_reply_cb_t) (DBusMessage *reply,
497 static int request_input_credentials(struct oc_private_data *data,
498 request_input_reply_cb_t cb);
501 static void request_input_pkcs_reply(DBusMessage *reply, void *user_data)
503 struct oc_private_data *data = user_data;
504 const char *password = NULL;
506 DBusMessageIter iter, dict;
509 connman_info("provider %p", data->provider);
514 err = vpn_agent_check_and_process_reply_error(reply, data->provider,
515 data->task, data->cb, data->user_data);
517 /* Ensure cb is called only once */
519 data->user_data = NULL;
523 if (!vpn_agent_check_reply_has_dict(reply))
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;
531 dbus_message_iter_recurse(&dict, &entry);
532 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
535 dbus_message_iter_get_basic(&entry, &key);
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)
542 dbus_message_iter_recurse(&entry, &value);
543 if (dbus_message_iter_get_arg_type(&value)
546 dbus_message_iter_get_basic(&value, &password);
547 vpn_provider_set_string_hide_value(data->provider, key,
551 dbus_message_iter_next(&dict);
554 if (data->connect_type != OC_CONNECT_PKCS || !password)
557 if (write_data(data->fd_in, password) != 0) {
558 connman_error("openconnect failed to take PKCS pass phrase on"
563 clear_provider_credentials(data->provider);
567 oc_connect_done(data, EACCES);
570 static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
573 struct oc_private_data *data;
574 const char *auth_failures[] = {
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",
582 "VPN service unavailable",
583 /* Problem with certificates */
584 "SSL connection failure",
585 "Creating SSL connection failed",
586 "SSL connection cancelled",
589 const char *conn_failures[] = {
590 "Failed to connect to",
591 "Failed to open HTTPS connection to",
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",
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",
606 const char *server_key_hash = " --servercert";
614 return G_SOURCE_REMOVE;
616 if (source && data->err_ch != source)
617 return G_SOURCE_REMOVE;
619 if ((condition & G_IO_IN)) {
623 if (!data->interactive) {
624 if (g_io_channel_read_line(source, &str, &len, NULL,
625 NULL) != G_IO_STATUS_NORMAL)
631 str = g_try_new0(char, OC_MAX_READBUF_LEN);
633 return G_SOURCE_REMOVE;
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);
639 if (status == G_IO_STATUS_EOF) {
641 } else if (status != G_IO_STATUS_NORMAL) {
646 /* Ignore control chars and digits at start */
647 if (!pos && (g_ascii_iscntrl(str[pos]) ||
648 g_ascii_isdigit(str[pos])))
651 /* Read zero length or no more to read */
652 if (!len || g_io_channel_get_buffer_condition(
653 source) != G_IO_IN ||
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.
663 if (str[pos] == '\n')
667 connman_info("openconnect: %s", str);
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;
674 bool allow_self_signed;
676 allow_self_signed = vpn_provider_get_boolean(
678 "OpenConnect.AllowSelfSignedCert",
681 if (allow_self_signed) {
682 position = strlen(server_key_hash) + 1;
683 fingerprint = g_strstrip(str + position);
685 connman_info("Set server key hash: \"%s\"",
688 vpn_provider_set_string(data->provider,
689 "OpenConnect.ServerCert",
693 * OpenConnect waits for "yes" or "no" as
694 * response to certificate acceptance request.
696 if (write_data(data->fd_in, "yes") != 0)
697 connman_error("openconnect: cannot "
698 "write answer to certificate "
702 connman_warn("Self signed certificate is not "
706 * Close IO channel to avoid deadlock as an
707 * answer is expected for the certificate
713 } else if (strv_contains_prefix(pkcs_failures, str)) {
714 connman_warn("PKCS failure: %s", str);
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);
722 if (err != -EINPROGRESS) {
728 } else if (strv_contains_prefix(auth_failures, str)) {
729 connman_warn("authentication failed: %s", str);
731 } else if (strv_contains_prefix(conn_failures, str)) {
732 connman_warn("connection failed: %s", str);
737 } else if (condition & (G_IO_ERR | G_IO_HUP)) {
738 connman_info("Err channel termination");
745 clear_provider_credentials(data->provider);
749 * This will trigger VPN_PROVIDER_ERROR_CONNECT_FAILED
750 * in vpn-provider.c:connect_cb().
756 oc_connect_done(data, err);
760 close_io_channel(data, source);
761 return G_SOURCE_REMOVE;
764 return G_SOURCE_CONTINUE;
767 static int run_connect(struct oc_private_data *data)
769 struct vpn_provider *provider;
770 struct connman_task *task;
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;
779 bool use_stdout = false;
787 provider = data->provider;
790 connman_info("provider %p task %p", provider, task);
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, "-")) {
801 connman_task_add_argument(task, "--cookie-on-stdin", NULL);
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, "-")) {
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",
824 /* Use stdout only when cookie is to be read. */
827 connman_task_add_argument(task, "--cookie-on-stdin",
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, "-")) {
843 connman_task_add_argument(task, "--user", username);
844 connman_task_add_argument(task, "--passwd-on-stdin", NULL);
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");
852 if (!certificate || !private_key) {
857 connman_task_add_argument(task, "--certificate", certificate);
858 connman_task_add_argument(task, "--sslkey", private_key);
860 case OC_CONNECT_PKCS:
861 certificate = vpn_provider_get_string(provider,
862 "OpenConnect.PKCSClientCert");
868 connman_task_add_argument(task, "--certificate", certificate);
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, "-"))
876 connman_task_add_argument(task, "--passwd-on-stdin", NULL);
880 vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
881 if (!vpnhost || !*vpnhost)
882 vpnhost = vpn_provider_get_string(provider, "Host");
884 task_append_config_data(provider, task);
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.
892 switch (data->connect_type) {
893 case OC_CONNECT_COOKIE:
895 case OC_CONNECT_COOKIE_WITH_USERPASS:
897 case OC_CONNECT_USERPASS:
899 case OC_CONNECT_PUBLICKEY:
900 connman_task_add_argument(task, "--syslog", NULL);
902 setting = vpn_provider_get_boolean(provider,
903 "OpenConnect.AllowSelfSignedCert",
905 setting_str = vpn_provider_get_string(provider,
906 "OpenConnect.ServerCert");
909 * Run in interactive mode if self signed certificates are
910 * allowed and there is no set server SHA1 fingerprint.
912 if (setting_str || !setting)
913 connman_task_add_argument(task, "--non-inter", NULL);
915 data->interactive = true;
917 case OC_CONNECT_PKCS:
918 data->interactive = true;
922 connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
924 connman_task_add_argument(task, "--interface", data->if_name);
926 connman_task_add_argument(task, (char *)vpnhost, NULL);
928 err = connman_task_run(task, oc_died, data, &data->fd_in, use_stdout ?
929 &fd_out : NULL, &fd_err);
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 "
944 case OC_CONNECT_USERPASS:
945 if (write_data(data->fd_in, password) != 0) {
946 connman_error("openconnect failed to take password on "
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");
960 if (write_data(data->fd_in, vpncookie) != 0) {
961 connman_error("openconnect failed to take "
968 case OC_CONNECT_PUBLICKEY:
970 case OC_CONNECT_PKCS:
971 if (!password || !g_strcmp0(password, "-"))
974 if (write_data(data->fd_in, password) != 0) {
975 connman_error("openconnect failed to take PKCS "
976 "pass phrase on stdin");
996 data->out_ch = g_io_channel_unix_new(fd_out);
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);
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,
1011 data->err_ch = g_io_channel_unix_new(fd_err);
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);
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);
1025 clear_provider_credentials(data->provider);
1030 static void request_input_append(DBusMessageIter *iter,
1031 const char *str_type, const char *str, void *user_data)
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);
1044 connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING,
1048 static void request_input_append_informational(DBusMessageIter *iter,
1051 request_input_append(iter, "string", "informational", user_data);
1054 static void request_input_append_mandatory(DBusMessageIter *iter,
1057 request_input_append(iter, "string", "mandatory", user_data);
1060 static void request_input_append_optional(DBusMessageIter *iter,
1063 request_input_append(iter, "string", "optional", user_data);
1066 static void request_input_append_password(DBusMessageIter *iter,
1069 request_input_append(iter, "password", "mandatory", user_data);
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)
1077 bool immutable = false;
1079 if (!provider || !dict || !function_cb || !key)
1082 str = vpn_provider_get_string(provider, key);
1083 /* Ignore empty informational content */
1084 if (!str && function_cb == request_input_append_informational)
1087 /* If value is "-", it is cleared by VPN agent */
1088 if (!g_strcmp0(str, "-"))
1092 immutable = vpn_provider_get_string_immutable(provider, key);
1095 /* Hide immutable password types */
1096 if (function_cb == request_input_append_password)
1099 /* Send immutable as informational */
1100 function_cb = request_input_append_informational;
1103 connman_dbus_dict_append_dict(dict, key, function_cb,
1104 str ? (void *)str : NULL);
1107 static void request_input_credentials_reply(DBusMessage *reply, void *user_data)
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;
1117 DBusMessageIter iter, dict;
1120 connman_info("provider %p", data->provider);
1125 err = vpn_agent_check_and_process_reply_error(reply, data->provider,
1126 data->task, data->cb, data->user_data);
1128 /* Ensure cb is called only once */
1130 data->user_data = NULL;
1134 if (!vpn_agent_check_reply_has_dict(reply))
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;
1142 dbus_message_iter_recurse(&dict, &entry);
1143 if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
1146 dbus_message_iter_get_basic(&entry, &key);
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)
1153 dbus_message_iter_recurse(&entry, &value);
1154 if (dbus_message_iter_get_arg_type(&value)
1155 != DBUS_TYPE_STRING)
1157 dbus_message_iter_get_basic(&value, &cookie);
1158 vpn_provider_set_string_hide_value(data->provider,
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)
1165 dbus_message_iter_recurse(&entry, &value);
1166 if (dbus_message_iter_get_arg_type(&value)
1167 != DBUS_TYPE_STRING)
1169 dbus_message_iter_get_basic(&value, &servercert);
1170 vpn_provider_set_string(data->provider, key,
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)
1178 dbus_message_iter_recurse(&entry, &value);
1179 if (dbus_message_iter_get_arg_type(&value)
1180 != DBUS_TYPE_STRING)
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)
1189 dbus_message_iter_recurse(&entry, &value);
1190 if (dbus_message_iter_get_arg_type(&value)
1191 != DBUS_TYPE_STRING)
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)
1201 dbus_message_iter_recurse(&entry, &value);
1202 if (dbus_message_iter_get_arg_type(&value)
1203 != DBUS_TYPE_STRING)
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)
1213 dbus_message_iter_recurse(&entry, &value);
1214 if (dbus_message_iter_get_arg_type(&value)
1215 != DBUS_TYPE_STRING)
1217 dbus_message_iter_get_basic(&value, &pkcspassword);
1218 vpn_provider_set_string_hide_value(data->provider, key,
1222 dbus_message_iter_next(&dict);
1225 switch (data->connect_type) {
1226 case OC_CONNECT_COOKIE:
1231 case OC_CONNECT_USERPASS:
1233 case OC_CONNECT_COOKIE_WITH_USERPASS:
1234 if (!username || !password)
1238 case OC_CONNECT_PUBLICKEY:
1239 break; // This should not be reached.
1240 case OC_CONNECT_PKCS:
1247 err = run_connect(data);
1248 if (err != -EINPROGRESS)
1254 oc_connect_done(data, EACCES);
1257 free_private_data(data);
1260 static int request_input_credentials(struct oc_private_data *data,
1261 request_input_reply_cb_t cb)
1263 DBusMessage *message;
1265 const char *agent_sender;
1266 const char *agent_path;
1267 const char *username;
1268 DBusMessageIter iter;
1269 DBusMessageIter dict;
1276 connman_info("provider %p", data->provider);
1278 agent = connman_agent_get_info(data->dbus_sender,
1279 &agent_sender, &agent_path);
1280 if (!data->provider || !agent || !agent_path)
1283 message = dbus_message_new_method_call(agent_sender, agent_path,
1284 VPN_AGENT_INTERFACE,
1289 dbus_message_iter_init_append(message, &iter);
1291 path = vpn_provider_get_path(data->provider);
1292 dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
1294 connman_dbus_dict_open(&iter, &dict);
1296 request_input_append_to_dict(data->provider, &dict,
1297 request_input_append_informational,
1298 "OpenConnect.CACert");
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
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");
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");
1319 if (vpn_provider_get_authentication_errors(data->provider))
1320 vpn_agent_append_auth_failure(&dict, data->provider, NULL);
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");
1329 * The authentication is done with username and password to get the
1330 * cookie for connection.
1332 case OC_CONNECT_COOKIE_WITH_USERPASS:
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);
1339 case OC_CONNECT_PUBLICKEY:
1341 case OC_CONNECT_PKCS:
1342 request_input_append_to_dict(data->provider, &dict,
1343 request_input_append_informational,
1344 "OpenConnect.PKCSClientCert");
1346 request_input_append_to_dict(data->provider, &dict,
1347 request_input_append_password,
1348 "OpenConnect.PKCSPassword");
1352 vpn_agent_append_host_and_name(&dict, data->provider);
1354 connman_dbus_dict_close(&iter, &dict);
1356 err = connman_agent_queue_message(data->provider, message,
1357 connman_timeout_input_request(), cb, data, agent);
1359 dbus_message_unref(message);
1361 if (err < 0 && err != -EBUSY) {
1362 connman_error("cannot send agent request, error: %d", err);
1366 return -EINPROGRESS;
1369 static enum oc_connect_type get_authentication_type(
1370 struct vpn_provider *provider)
1373 enum oc_connect_type type;
1375 auth = vpn_provider_get_string(provider, "OpenConnect.AuthType");
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]);
1388 /* Default to cookie */
1389 return OC_CONNECT_COOKIE;
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)
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;
1405 connman_info("provider %p task %p", provider, task);
1407 data = g_try_new0(struct oc_private_data, 1);
1411 vpn_provider_set_plugin_data(provider, data);
1412 data->provider = vpn_provider_ref(provider);
1414 data->if_name = g_strdup(if_name);
1415 data->dbus_sender = g_strdup(dbus_sender);
1417 data->user_data = user_data;
1418 data->connect_type = get_authentication_type(provider);
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, "-"))
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, "-"))
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 */
1443 username = vpn_provider_get_string(provider,
1444 "OpenConnect.Username");
1445 password = vpn_provider_get_string(provider,
1446 "OpenConnect.Password");
1448 if (!username || !password ||
1449 !g_strcmp0(username, "-") ||
1450 !g_strcmp0(password, "-"))
1452 } else if (!g_strcmp0(vpncookie, "-")) {
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");
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);
1471 case OC_CONNECT_PKCS:
1472 certificate = vpn_provider_get_string(provider,
1473 "OpenConnect.PKCSClientCert");
1475 connman_warn("missing PKCS certificate");
1476 oc_connect_done(data, EACCES);
1477 free_private_data(data);
1484 return run_connect(data);
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);
1498 static void oc_disconnect(struct vpn_provider *provider)
1500 connman_info("provider %p", provider);
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.
1510 connman_agent_cancel(provider);
1513 static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
1515 const char *save_group;
1519 save_group = vpn_provider_get_save_group(provider);
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);
1528 g_key_file_set_string(keyfile, save_group,
1529 oc_options[i].cm_opt, option);
1536 static int oc_error_code(struct vpn_provider *provider, int exit_code)
1538 connman_info("%d", exit_code);
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.
1547 switch (exit_code) {
1549 /* Cookie has failed */
1550 clear_provider_credentials(provider);
1551 return VPN_PROVIDER_ERROR_LOGIN_FAILED;
1555 return VPN_PROVIDER_ERROR_UNKNOWN;
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)
1565 if (g_str_has_prefix(key, "CISCO_SPLIT_INC_")) {
1567 start = key + strlen("CISCO_SPLIT_INC_");
1568 } else if (g_str_has_prefix(key, "CISCO_IPV6_SPLIT_INC_")) {
1570 start = key + strlen("CISCO_IPV6_SPLIT_INC_");
1574 *idx = g_ascii_strtoull(start, &end, 10);
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;
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,
1594 .route_env_parse = oc_route_env_parse,
1597 static int openconnect_init(void)
1599 return vpn_register("openconnect", &vpn_driver, OPENCONNECT);
1602 static void openconnect_exit(void)
1604 vpn_unregister("openconnect");
1607 CONNMAN_PLUGIN_DEFINE(openconnect, "OpenConnect VPN plugin", VERSION,
1608 CONNMAN_PLUGIN_PRIORITY_DEFAULT, openconnect_init, openconnect_exit)