Send connman mailing list submissions to
[email protected]
To subscribe or unsubscribe via email, send a message with subject or
body 'help' to
[email protected]
You can reach the person managing the list at
[email protected]
When replying, please edit your Subject line so it is more specific
than "Re: Contents of connman digest..."
Today's Topics:
1. Re: [PATCH v3 6/8] openconnect: Use interactive mode when input to stdin
is required
(Jussi Laakkonen)
2. [PATCH v5 5/8] openconnect: Rewrite plugin to support more auth methods
and protocols
(Jussi Laakkonen)
----------------------------------------------------------------------
Date: Wed, 9 Oct 2019 15:43:23 +0300
From: Jussi Laakkonen <[email protected]>
Subject: Re: [PATCH v3 6/8] openconnect: Use interactive mode when
input to stdin is required
To: David Woodhouse <[email protected]>, [email protected]
Message-ID: <[email protected]>
Content-Type: text/plain; charset=utf-8; format=flowed
Hi David,
Thanks for the input, this is my first touch with OpenConnect. I wasn't
really aware that there is a library to use with it.
Is the point of the OpenConnect library to assist in getting the cookie
regardless of the authentication type used. So the intention is to do
the authentication first, then always use the cookie when connecting?
With quick look it seems that switching to that library from this
screenscraping approach I've built would not be straightforward.
However, it might simplify things there. But now I do not have time for
a yet another rewrite. Maybe somewhere in the near future I'll look that
library approach more closely and see how it can be used here and submit
the results for review.
Sincerely,
Jussi Laakkonen
On 10/9/19 12:42 PM, David Woodhouse wrote:
> On Wed, 2019-10-09 at 11:34 +0300, Jussi Laakkonen wrote:
>> Disable syslog (--syslog) and use interactive mode when OpenConnect
>> requires input from user. This is the case when PKCS file is encrypted
>> and pass phrase is requested. Changed also to prompt for the PKCS
>> password only when needed, instead of having it as mandatory for the
>> pkcs authentication method. Also, in case self signed certificates are
>> allowed the answer needs to be written to OpenConnect, thus, interactive
>> mode is required.
>
> Don't screenscrape the openconnect command line tool.
>
> Use libopenconnect instead. It'll call your "user interaction" function
> with clear arguments for everything it wants.
>
> See the NetworkManager auth-dialog (or indeed openconnect's main.c) for
> an example.
>
>
------------------------------
Date: Wed, 9 Oct 2019 16:37:06 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH v5 5/8] openconnect: Rewrite plugin to support more
auth methods and protocols
To: [email protected]
Cc: Santtu Lakkala <[email protected]>
Message-ID: <[email protected]>
Co-authored-by: Santtu Lakkala <[email protected]>
The purpose of this rewrite is to extend OpenConnect plugin to support
more authentication methods and support settings for them. Also VPN
agent use in the plugin is improved. In order to support self signed
certificate and PKCS pass phrase requests, and to improve detection
of errors stderr is monitored and reacted accordingly. Furthermore, all
the three protocols are supported via provider settings.
Following new option strings were added:
NAME TYPE (internal) VIA
OpenConnect.AllowSelfSignedCert string (bool) Provider
OpenConnect.AuthType string Provider
OpenConnect.DisableIPv6 string (bool) Provider
OpenConnect.NoDTLS string (bool) Provider
OpenConnect.NoHTTPKeepalive string (bool) Provider
OpenConnect.PKCSClientCert string (path) Provider
OpenConnect.PKCSPassword string Agent
OpenConnect.Protocol string Provider
OpenConnect.Usergroup string Provider
OpenConnect.UserPrivateKey string (path) Provider
VPN.MTU string (int) Provider
The supported types for authentication types ("OpenConnect.AuthType")
are:
- Cookie: "cookie"
- Use cookie after username and password auth: "cookie_with_userpass"
- Username and password: "userpass"
- Certificate using public key: "publickey"
- Use PKCS#1/#8/#12 file: "pkcs"
Without defining the authentication type, OpenConnect defaults to "cookie".
The value of the "OpenConnect.AuthType" is saved to provider config and
resulting provider settings file.
In case the "cookie_with_userpass" is used as authentication method
first the username and password are used to get the OpenConnect cookie
(only authentication is performed, OpenConnect is run with --cookieonly)
and then the resulting cookie is used in the next connect. Only with
this authentication method stdout is monitored for cookie string.
With "pkcs" the password is queried via agent for the PKCS file. This
is implemented as separate from the public key authentication as the
separation of the actions required for the authentication are more clear
clear this way. PKCS decryption errors are sent as priority data to
stderr, which must be read in order to detect it. It is imperative to
close the io channel as well since OpenConnect will write a new PKCS
pass phrase request to stderr as well, without newline, which will block
until timeout. It is better to close the channel in case of decryption
error to avoid connman-vpnd freezing.
Username, Password and PKCSPassword are to be retrieved via VPN agent.
These are saved as hidden provider values and are not stored to disk.
ClientCert, UserPrivateKey and PKCSClienCert are all paths to the
corresponding files on local filesystem.
Authentication failures are sent to VPN agent to indicate that saved
credentials should be cleared. For this the changes to auth and
connection error tracking in vpn-provider.c, and the changes to
vpn-agent.c are utilized.
In order to be compatible with the previous version of the plugin the
default authentication, "cookie" is left as is from VPN agent part
except the VPN server after authentication is set as optional. With
other authentication methods server SHA1 fingerprint and VPN server
after authentication are to be set into provider settings.
Option "AllowSelfSignedCert" can be set as string "true" or "false".
The value defaults "false" if value is omitted or invalid. Self signed
server certificates have to be explicitly allowed. If the value is false
the --servercert reported by OpenConnect via stderr is not saved to
provider settings and connection is terminated. Followed by this
OpenConnect requests a reply to indicate that the certificate needs to
be approved, or denied, and depending on the option, answer is written
to stdin ("yes") if self signed certs are allowed and otherwise the
connection is terminated. In case the value is false the task parameter
"--servercert" is also omitted when running the task. In case the value
is true, the self signed certificate is saved, if returned by the
OpenConnect client and the server certificate SHA1 fingerprint is stored
to OpenConnect.ServerCert in provider settings.
The following were added to OpenConnect startup parameters if defined in
provider settings as "true".
PROVIDER SETTING PARAMETER
OpenConnect.DisableIPv6 --disable-ipv6
OpenConnect.NoDTLS --no-dtls
OpenConnect.NoHTTPKeepalive --no-http-keepalive
VPN.MTU --base-mtu
Additionally, OpenConnect is set to use syslog for all connection related
output with --syslog. This is because with syslog enabled only the
cookie is delivered via stdout.
All the three different protocols supported by OpenConnect can be
selected with provider setting OpenConnect.Protocol.
VALUE DEFINITION
anyconnect Cisco AnyConnect (default)
nc Juniper Network Connect / Pulse Secure
gp Palo Alto Networks GlobalProtect
---
Changes since V2:
* Use stdout only when cookie is to be read from it.
* React to "Failed to obtain VPN cookie" as an authentication error.
* Reorder request_input_append() input vars to be more clear.
* Fix cookie use with VPN agent, retrieve from VPN agent if cookie is "-".
* Do not use OpenConnect.VPNHost if it is empty string.
* Save OpenConnect.VPNHost in provider settings.
Changes since V3:
* Forgot to add Santtu to the V2 of patches as co-author, added now.
Changes since V4:
* Replace PKCS#12 with PKCS already in this commit for clarity.
* Improve setting use and saving to be more consistent and sort the options.
* Replace unnecessary save group retrieval with one vpn_provider_get_string.
Changes since V5:
* Remove unnecessary parameter setting in run_connect() - these were set to
be handled with V4 of this commit with task_append_config_data().
vpn/plugins/openconnect.c | 1093 +++++++++++++++++++++++++++++++------
1 file changed, 940 insertions(+), 153 deletions(-)
diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c
index 21224c4a..1d87d120 100644
--- a/vpn/plugins/openconnect.c
+++ b/vpn/plugins/openconnect.c
@@ -3,6 +3,7 @@
* ConnMan VPN daemon
*
* Copyright (C) 2007-2013 Intel Corporation. All rights reserved.
+ * Copyright (C) 2019 Jolla Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
@@ -48,24 +49,134 @@
#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+enum opt_type {
+ OPT_STRING = 0,
+ OPT_BOOL = 1,
+};
+
struct {
- const char *cm_opt;
- const char *oc_opt;
- char has_value;
+ const char *cm_opt;
+ const char *oc_opt;
+ bool has_value;
+ bool enabled; // Use as task parameter
+ enum opt_type type;
} oc_options[] = {
- { "OpenConnect.NoCertCheck", "--no-cert-check", 0 },
+ { "OpenConnect.AllowSelfSignedCert", NULL, 1, 0, OPT_BOOL},
+ { "OpenConnect.AuthType", NULL, 1, 0, OPT_STRING},
+ { "OpenConnect.CACert", "--cafile", 1, 1, OPT_STRING},
+ { "OpenConnect.ClientCert", NULL, 1, 0, OPT_STRING},
+ { "OpenConnect.DisableIPv6", "--disable-ipv6", 1, 1, OPT_BOOL},
+ { "OpenConnect.PKCSClientCert", NULL, 1, 0, OPT_STRING},
+ { "OpenConnect.Protocol", "--protocol", 1, 1, OPT_STRING},
+ /* --no-cert-check is disabled in openconnect 8.02 */
+ { "OpenConnect.NoCertCheck", "--no-cert-check", 0, 0, OPT_BOOL},
+ { "OpenConnect.NoHTTPKeepalive", "--no-http-keepalive", 1, 1, OPT_BOOL},
+ { "OpenConnect.NoDTLS", "--no-dtls", 1, 1, OPT_BOOL},
+ { "OpenConnect.ServerCert", "--servercert", 1, 1, OPT_STRING},
+ { "OpenConnect.Usergroup", "--usergroup", 1, 1, OPT_STRING},
+ { "OpenConnect.UserPrivateKey", NULL, 1, 0, OPT_STRING},
+ { "VPN.MTU", "--base-mtu", 1, 1, OPT_STRING},
};
+enum oc_connect_type {
+ OC_CONNECT_COOKIE = 0,
+ OC_CONNECT_COOKIE_WITH_USERPASS,
+ OC_CONNECT_USERPASS,
+ OC_CONNECT_PUBLICKEY,
+ OC_CONNECT_PKCS,
+};
+
+static const char *connect_types[] = {"cookie", "cookie_with_userpass",
+ "userpass", "publickey", "pkcs", NULL};
+static const char *protocols[] = { "anyconnect", "nc", "gp", NULL};
+
struct oc_private_data {
struct vpn_provider *provider;
struct connman_task *task;
char *if_name;
+ char *dbus_sender;
vpn_provider_connect_cb_t cb;
void *user_data;
+ int fd_in;
+ int out_ch_id;
+ int err_ch_id;
+ GIOChannel *out_ch;
+ GIOChannel *err_ch;
+ enum oc_connect_type connect_type;
};
+static bool is_valid_protocol(const char* protocol)
+{
+ if (!protocol || !*protocol)
+ return false;
+
+ return g_strv_contains(protocols, protocol);
+}
+
+static void oc_connect_done(struct oc_private_data *data, int err)
+{
+ connman_info("data %p err %d/%s", data, err, strerror(err));
+
+ if (data && data->cb) {
+ vpn_provider_connect_cb_t cb = data->cb;
+ void *user_data = data->user_data;
+
+ /* Make sure we don't invoke this callback twice */
+ data->cb = NULL;
+ data->user_data = NULL;
+ cb(data->provider, user_data, err);
+ }
+}
+
+static void close_io_channel(struct oc_private_data *data, GIOChannel *channel)
+{
+ int id = 0;
+
+ connman_info("data %p channel %p", data, channel);
+
+ if (!data || !channel)
+ return;
+
+ if (data->out_ch == channel) {
+ id = data->out_ch_id;
+ data->out_ch = NULL;
+ data->out_ch_id = 0;
+ } else if (data->err_ch == channel) {
+ id = data->err_ch_id;
+ data->err_ch = NULL;
+ data->err_ch_id = 0;
+ } else {
+ return;
+ }
+
+ if (id)
+ g_source_remove(id);
+
+ g_io_channel_shutdown(channel, FALSE, NULL);
+ g_io_channel_unref(channel);
+}
+
static void free_private_data(struct oc_private_data *data)
{
+ connman_info("data %p", data);
+
+ if (!data || !data->provider)
+ return;
+
+ connman_info("provider %p", data->provider);
+
+ if (vpn_provider_get_plugin_data(data->provider) == data)
+ vpn_provider_set_plugin_data(data->provider, NULL);
+
+ vpn_provider_unref(data->provider);
+
+ if (data->fd_in > 0)
+ close(data->fd_in);
+ data->fd_in = -1;
+ close_io_channel(data, data->out_ch);
+ close_io_channel(data, data->err_ch);
+
+ g_free(data->dbus_sender);
g_free(data->if_name);
g_free(data);
}
@@ -73,17 +184,51 @@ static void free_private_data(struct oc_private_data *data)
static int task_append_config_data(struct vpn_provider *provider,
struct connman_task *task)
{
- const char *option;
+ const char *option = NULL;
int i;
for (i = 0; i < (int)ARRAY_SIZE(oc_options); i++) {
- if (!oc_options[i].oc_opt)
+ if (!oc_options[i].oc_opt || !oc_options[i].enabled)
continue;
- option = vpn_provider_get_string(provider,
- oc_options[i].cm_opt);
- if (!option)
- continue;
+ if (oc_options[i].has_value) {
+ option = vpn_provider_get_string(provider,
+ oc_options[i].cm_opt);
+ if (!option)
+ continue;
+
+ /* Add boolean type values only if set as true. */
+ if (oc_options[i].type == OPT_BOOL) {
+ if (!vpn_provider_get_boolean(provider,
+ oc_options[i].cm_opt,
+ false))
+ continue;
+
+ /* No option is set for boolean type values. */
+ option = NULL;
+ }
+
+ /* Skip protocol if it is invalid. */
+ if (!g_strcmp0(oc_options[i].cm_opt,
+ "OpenConnect.Protocol")) {
+ if (!is_valid_protocol(option))
+ continue;
+ }
+ }
+
+ /*
+ * Add server certificate fingerprint only when self signed
+ * certificates are explicitly allowed. Using --servercert as
+ * parameter will accept any server with matching fingerprint,
+ * which would disregard the setting of AllowSelfSignedCert.
+ */
+ if (!g_strcmp0(oc_options[i].cm_opt,
+ "OpenConnect.ServerCert")) {
+ if (!vpn_provider_get_boolean(provider,
+ "OpenConnect.AllowSelfSignedCert",
+ false))
+ continue;
+ }
if (connman_task_add_argument(task,
oc_options[i].oc_opt,
@@ -103,6 +248,11 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider
*provider)
char *netmask = NULL, *gateway = NULL;
unsigned char prefix_len = 0;
struct connman_ipaddress *ipaddress;
+ struct oc_private_data *data;
+
+ connman_info("provider %p", provider);
+
+ data = vpn_provider_get_plugin_data(provider);
dbus_message_iter_init(msg, &iter);
@@ -111,6 +261,7 @@ static int oc_notify(DBusMessage *msg, struct vpn_provider
*provider)
if (!provider) {
connman_error("No provider found");
+ oc_connect_done(data, ENOENT);
return VPN_STATE_FAILURE;
}
@@ -214,106 +365,556 @@ static int oc_notify(DBusMessage *msg, struct
vpn_provider *provider)
g_free(domain);
connman_ipaddress_free(ipaddress);
+ oc_connect_done(data, 0);
return VPN_STATE_CONNECT;
}
-static int run_connect(struct vpn_provider *provider,
- struct connman_task *task, const char *if_name,
- vpn_provider_connect_cb_t cb, void *user_data)
+static ssize_t full_write(int fd, const void *buf, size_t len)
{
- const char *vpnhost, *vpncookie, *servercert, *mtu;
- int fd, err = 0, len;
+ ssize_t byte_write;
+
+ while (len) {
+ byte_write = write(fd, buf, len);
+ if (byte_write < 0) {
+ connman_error("failed to write config to openconnect: "
+ " %s\n", strerror(errno));
+ return byte_write;
+ }
+ len -= byte_write;
+ buf += byte_write;
+ }
- vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
- if (!vpnhost)
- vpnhost = vpn_provider_get_string(provider, "Host");
- vpncookie = vpn_provider_get_string(provider, "OpenConnect.Cookie");
- servercert = vpn_provider_get_string(provider,
- "OpenConnect.ServerCert");
+ return len;
+}
- if (!vpncookie || !servercert) {
- err = -EINVAL;
- goto done;
+static ssize_t write_data(int fd, const char *data)
+{
+ gchar *buf;
+ ssize_t len;
+
+ if (!data || !*data)
+ return -1;
+
+ buf = g_strdup_printf("%s\n", data);
+
+ len = full_write(fd, buf, strlen(buf));
+
+ g_free(buf);
+
+ return len;
+}
+
+static void oc_died(struct connman_task *task, int exit_code, void *user_data)
+{
+ struct oc_private_data *data = user_data;
+
+ connman_info("task %p data %p exit_code %d user_data %p", task, data,
+ exit_code, user_data);
+
+ if (!data)
+ return;
+
+ if (data->provider) {
+ connman_agent_cancel(data->provider);
+
+ if (task)
+ vpn_died(task, exit_code, data->provider);
}
- task_append_config_data(provider, task);
+ free_private_data(data);
+}
- connman_task_add_argument(task, "--servercert", servercert);
+static gboolean io_channel_out_cb(GIOChannel *source, GIOCondition condition,
+ gpointer user_data)
+{
+ struct oc_private_data *data;
+ char *str;
+
+ data = user_data;
+
+ if (data->out_ch != source)
+ return G_SOURCE_REMOVE;
+
+ if ((condition & G_IO_IN) &&
+ g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
+ G_IO_STATUS_NORMAL) {
+
+ g_strchomp(str);
+
+ /* Only cookie is printed to stdout */
+ vpn_provider_set_string_hide_value(data->provider,
+ "OpenConnect.Cookie", str);
+
+ g_free(str);
+ } else if (condition & (G_IO_ERR | G_IO_HUP)) {
+ connman_info("Out channel termination");
+ close_io_channel(data, source);
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
- mtu = vpn_provider_get_string(provider, "VPN.MTU");
+static bool strv_contains_prefix(const char *strv[], const char *str)
+{
+ int i;
- if (mtu)
- connman_task_add_argument(task, "--mtu", (char *)mtu);
+ if (!strv || !str || !*str)
+ return false;
+
+ for (i = 0; strv[i]; i++) {
+ if (g_str_has_prefix(str, strv[i]))
+ return true;
+ }
+ return false;
+}
+
+static void clear_provider_credentials(struct vpn_provider *provider)
+{
+ const char *keys[] = { "OpenConnect.Username",
+ "OpenConnect.Password",
+ "OpenConnect.PKCSPassword",
+ "OpenConnect.Cookie",
+ NULL
+ };
+ int i;
+
+ connman_info("provider %p", provider);
+
+ for (i = 0; keys[i]; i++) {
+ if (!vpn_provider_get_string_immutable(provider, keys[i]))
+ vpn_provider_set_string_hide_value(provider, keys[i],
+ "-");
+ }
+}
+
+static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
+ gpointer user_data)
+{
+ struct oc_private_data *data;
+ const char *auth_failures[] = {
+ /* Login failed */
+ "Got HTTP response: HTTP/1.1 401 Unauthorized",
+ "Failed to obtain WebVPN cookie",
+ /* Cookie not valid */
+ "Got inappropriate HTTP CONNECT response: "
+ "HTTP/1.1 401 Unauthorized",
+ /* Invalid cookie */
+ "VPN service unavailable",
+ /* Problem with certificates */
+ "SSL connection failure",
+ "Creating SSL connection failed",
+ "SSL connection cancelled",
+ NULL
+ };
+ const char *conn_failures[] = {
+ "Failed to connect to",
+ "Failed to open HTTPS connection to",
+ NULL
+ };
+ const char *pkcs_failure = "Failed to decrypt PKCS";
+ const char *server_key_hash = " --servercert";
+ char *str;
+ bool close = false;
+ int err = 0;
+
+ data = user_data;
+
+ if (!data)
+ return G_SOURCE_REMOVE;
+
+ if (source && data->err_ch != source)
+ return G_SOURCE_REMOVE;
+
+ if ((condition & G_IO_IN) &&
+ g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
+ G_IO_STATUS_NORMAL) {
+ str[strlen(str) - 1] = '\0';
+
+ connman_info("openconnect: %s", str);
+
+ if (g_str_has_prefix(str, server_key_hash)) {
+ int position;
+ bool allow_self_signed;
+
+ allow_self_signed = vpn_provider_get_boolean(
+ data->provider,
+ "OpenConnect.AllowSelfSignedCert",
+ false);
+
+ if (allow_self_signed) {
+ position = strlen(server_key_hash) + 1;
+
+ connman_info("Set server key hash: \"%s\"",
+ str + position);
+
+ vpn_provider_set_string(data->provider,
+ "OpenConnect.ServerCert",
+ str + position);
+
+ /*
+ * OpenConnect waits for "yes" or "no" as
+ * response to certificate acceptance request.
+ */
+ if (write_data(data->fd_in, "yes") != 0)
+ connman_error("cannot write answer to "
+ "certificate accept request");
+
+ } else {
+ connman_warn("Self signed certificate is not "
+ " allowed");
+
+ /*
+ * Close IO channel to avoid deadlock as an
+ * answer is expected for the certificate
+ * accept request.
+ */
+ close = true;
+ err = ECONNREFUSED;
+ }
+ } else if (g_str_has_prefix(str, pkcs_failure)) {
+ connman_warn("invalid PKCS password: %s", str);
+ /*
+ * Close IO channel to avoid deadlock. This is because
+ * after PKCS file decryption fails OpenConnect will
+ * wait for a new pass phrase to be written to stdin,
+ * blocking vpnd as well. Instead of waiting for a
+ * timeout it is better to close the IO channel here.
+ */
+ close = true;
+ err = EACCES;
+ } else if (strv_contains_prefix(auth_failures, str)) {
+ connman_warn("authentication failed: %s", str);
+ err = EACCES;
+ } else if (strv_contains_prefix(conn_failures, str)) {
+ connman_warn("connection failed: %s", str);
+ err = ECONNREFUSED;
+ }
+
+ g_free(str);
+ } else if (condition & (G_IO_ERR | G_IO_HUP)) {
+ connman_info("Err channel termination");
+ close = true;
+ }
+
+ if (err) {
+ switch (err) {
+ case EACCES:
+ clear_provider_credentials(data->provider);
+ break;
+ case ECONNREFUSED:
+ /*
+ * This will trigger VPN_PROVIDER_ERROR_CONNECT_FAILED
+ * in vpn-provider.c:connect_cb().
+ */
+ default:
+ break;
+ }
+
+ oc_connect_done(data, err);
+ }
+
+ if (close) {
+ close_io_channel(data, source);
+ return G_SOURCE_REMOVE;
+ }
+
+ return G_SOURCE_CONTINUE;
+}
+
+static int run_connect(struct oc_private_data *data)
+{
+ struct vpn_provider *provider;
+ struct connman_task *task;
+ const char *vpnhost;
+ const char *vpncookie = NULL;
+ const char *username;
+ const char *password = NULL;
+ const char *certificate = NULL;
+ const char *private_key;
+ bool use_stdout = false;
+ int fd_out;
+ int fd_err;
+ int err = 0;
+
+ if (!data)
+ return -EINVAL;
+
+ provider = data->provider;
+ task = data->task;
+
+ connman_info("provider %p task %p", provider, task);
+
+ switch (data->connect_type) {
+ case OC_CONNECT_COOKIE:
+ vpncookie = vpn_provider_get_string(provider,
+ "OpenConnect.Cookie");
+ if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
+ err = -EACCES;
+ goto done;
+ }
+
+ connman_task_add_argument(task, "--cookie-on-stdin", NULL);
+ break;
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
+ vpncookie = vpn_provider_get_string(provider,
+ "OpenConnect.Cookie");
+ /* No cookie set yet, username and password used first */
+ if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
+ username = vpn_provider_get_string(provider,
+ "OpenConnect.Username");
+ password = vpn_provider_get_string(provider,
+ "OpenConnect.Password");
+ if (!username || !password ||
+ !g_strcmp0(username, "-") ||
+ !g_strcmp0(password, "-")) {
+ err = -EACCES;
+ goto done;
+ }
+
+ connman_task_add_argument(task, "--cookieonly", NULL);
+ connman_task_add_argument(task, "--user", username);
+ connman_task_add_argument(task, "--passwd-on-stdin",
+ NULL);
+ } else {
+ connman_task_add_argument(task, "--cookie-on-stdin",
+ NULL);
+ }
+
+ /* Use stdout only when cookie is to be read. */
+ use_stdout = true;
+
+ break;
+ case OC_CONNECT_USERPASS:
+ username = vpn_provider_get_string(provider,
+ "OpenConnect.Username");
+ password = vpn_provider_get_string(provider,
+ "OpenConnect.Password");
+ if (!username || !password || !g_strcmp0(username, "-") ||
+ !g_strcmp0(password, "-")) {
+ err = -EACCES;
+ goto done;
+ }
+
+ connman_task_add_argument(task, "--user", username);
+ connman_task_add_argument(task, "--passwd-on-stdin", NULL);
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ certificate = vpn_provider_get_string(provider,
+ "OpenConnect.ClientCert");
+ private_key = vpn_provider_get_string(provider,
+ "OpenConnect.UserPrivateKey");
+
+ if (!certificate || !private_key) {
+ err = -EACCES;
+ goto done;
+ }
+
+ connman_task_add_argument(task, "--certificate", certificate);
+ connman_task_add_argument(task, "--sslkey", private_key);
+ break;
+ case OC_CONNECT_PKCS:
+ certificate = vpn_provider_get_string(provider,
+ "OpenConnect.PKCSClientCert");
+ password = vpn_provider_get_string(data->provider,
+ "OpenConnect.PKCSPassword");
+ if (!certificate || !password) {
+ err = -EACCES;
+ goto done;
+ }
+
+ connman_task_add_argument(task, "--certificate", certificate);
+ connman_task_add_argument(task, "--passwd-on-stdin", NULL);
+ break;
+ }
+
+ vpnhost = vpn_provider_get_string(provider, "OpenConnect.VPNHost");
+ if (!vpnhost || !*vpnhost)
+ vpnhost = vpn_provider_get_string(provider, "Host");
+
+ task_append_config_data(provider, task);
+
+ connman_task_add_argument(task, "--non-inter", NULL);
+
+ /* Output all to syslog to output cookie only to stdout */
connman_task_add_argument(task, "--syslog", NULL);
- connman_task_add_argument(task, "--cookie-on-stdin", NULL);
connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
- connman_task_add_argument(task, "--interface", if_name);
+ connman_task_add_argument(task, "--interface", data->if_name);
connman_task_add_argument(task, (char *)vpnhost, NULL);
- err = connman_task_run(task, vpn_died, provider,
- &fd, NULL, NULL);
+ err = connman_task_run(task, oc_died, data, &data->fd_in, use_stdout ?
+ &fd_out : NULL, &fd_err);
if (err < 0) {
- connman_error("openconnect failed to start");
err = -EIO;
goto done;
}
- len = strlen(vpncookie);
- if (write(fd, vpncookie, len) != (ssize_t)len ||
- write(fd, "\n", 1) != 1) {
- connman_error("openconnect failed to take cookie on stdin");
- err = -EIO;
+ switch (data->connect_type) {
+ case OC_CONNECT_COOKIE:
+ if (write_data(data->fd_in, vpncookie) != 0) {
+ connman_error("openconnect failed to take cookie on "
+ "stdin");
+ err = -EIO;
+ }
+
+ break;
+ case OC_CONNECT_USERPASS:
+ if (write_data(data->fd_in, password) != 0) {
+ connman_error("openconnect failed to take password on "
+ "stdin");
+ err = -EIO;
+ }
+
+ break;
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
+ if (!vpncookie || !g_strcmp0(vpncookie, "-")) {
+ if (write_data(data->fd_in, password) != 0) {
+ connman_error("openconnect failed to take "
+ "password on stdin");
+ err = -EIO;
+ }
+ } else {
+ if (write_data(data->fd_in, vpncookie) != 0) {
+ connman_error("openconnect failed to take "
+ "cookie on stdin");
+ err = -EIO;
+ }
+ }
+
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ break;
+ case OC_CONNECT_PKCS:
+ if (write_data(data->fd_in, password) != 0) {
+ connman_error("openconnect failed to take PKCS "
+ "password on stdin");
+ err = -EIO;
+ }
+
+ break;
+ }
+
+ if (err) {
+ if (fd_out > 0)
+ close(fd_out);
+
+ if (fd_err > 0)
+ close(fd_err);
+
goto done;
}
+ err = -EINPROGRESS;
+
+ if (use_stdout) {
+ data->out_ch = g_io_channel_unix_new(fd_out);
+ data->out_ch_id = g_io_add_watch(data->out_ch,
+ G_IO_IN | G_IO_ERR | G_IO_HUP,
+ (GIOFunc)io_channel_out_cb, data);
+ }
+
+ data->err_ch = g_io_channel_unix_new(fd_err);
+ data->err_ch_id = g_io_add_watch(data->err_ch,
+ G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_PRI,
+ (GIOFunc)io_channel_err_cb, data);
+
done:
- if (cb)
- cb(provider, user_data, err);
+ clear_provider_credentials(data->provider);
return err;
}
-static void request_input_append_informational(DBusMessageIter *iter,
- void *user_data)
+static void request_input_append(DBusMessageIter *iter,
+ const char *str_type, const char *str, void *user_data)
{
- const char *str;
+ const char *string;
- str = "string";
- connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING, &str);
+ connman_dbus_dict_append_basic(iter, "Type",
+ DBUS_TYPE_STRING, &str_type);
+ connman_dbus_dict_append_basic(iter, "Requirement",
+ DBUS_TYPE_STRING, &str);
- str = "informational";
- connman_dbus_dict_append_basic(iter, "Requirement", DBUS_TYPE_STRING,
- &str);
+ if (!user_data)
+ return;
- str = user_data;
- connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING, &str);
+ string = user_data;
+ connman_dbus_dict_append_basic(iter, "Value", DBUS_TYPE_STRING,
+ &string);
+}
+
+static void request_input_append_informational(DBusMessageIter *iter,
+ void *user_data)
+{
+ request_input_append(iter, "string", "informational", user_data);
}
static void request_input_append_mandatory(DBusMessageIter *iter,
void *user_data)
{
- char *str = "string";
+ request_input_append(iter, "string", "mandatory", user_data);
+}
- connman_dbus_dict_append_basic(iter, "Type",
- DBUS_TYPE_STRING, &str);
- str = "mandatory";
- connman_dbus_dict_append_basic(iter, "Requirement",
- DBUS_TYPE_STRING, &str);
+static void request_input_append_optional(DBusMessageIter *iter,
+ void *user_data)
+{
+ request_input_append(iter, "string", "optional", user_data);
+}
+
+static void request_input_append_password(DBusMessageIter *iter,
+ void *user_data)
+{
+ request_input_append(iter, "password", "mandatory", user_data);
+}
+
+static void request_input_append_to_dict(struct vpn_provider *provider,
+ DBusMessageIter *dict,
+ connman_dbus_append_cb_t function_cb, const char *key)
+{
+ const char *str;
+ bool immutable = false;
+
+ if (!provider || !dict || !function_cb || !key)
+ return;
+
+ str = vpn_provider_get_string(provider, key);
+ /* Ignore empty informational content */
+ if (!str && function_cb == request_input_append_informational)
+ return;
+
+ /* If value is "-", it is cleared by VPN agent */
+ if (!g_strcmp0(str, "-"))
+ str = NULL;
+
+ if (str)
+ immutable = vpn_provider_get_string_immutable(provider, key);
+
+ if (immutable) {
+ /* Hide immutable password types */
+ if (function_cb == request_input_append_password)
+ str = "********";
+
+ /* Send immutable as informational */
+ function_cb = request_input_append_informational;
+ }
+
+ connman_dbus_dict_append_dict(dict, key, function_cb,
+ str ? (void *)str : NULL);
}
-static void request_input_cookie_reply(DBusMessage *reply, void *user_data)
+static void request_input_credentials_reply(DBusMessage *reply, void
*user_data)
{
struct oc_private_data *data = user_data;
char *cookie = NULL, *servercert = NULL, *vpnhost = NULL;
- char *key;
+ const char *username = NULL, *password = NULL, *pkcspassword = NULL;
+ const char *key;
DBusMessageIter iter, dict;
int err;
- DBG("provider %p", data->provider);
+ connman_info("provider %p", data->provider);
if (!reply)
goto err;
@@ -353,7 +954,6 @@ static void request_input_cookie_reply(DBusMessage *reply,
void *user_data)
dbus_message_iter_get_basic(&value, &cookie);
vpn_provider_set_string_hide_value(data->provider,
key, cookie);
-
} else if (g_str_equal(key, "OpenConnect.ServerCert")) {
dbus_message_iter_next(&entry);
if (dbus_message_iter_get_arg_type(&entry)
@@ -378,44 +978,99 @@ static void request_input_cookie_reply(DBusMessage
*reply, void *user_data)
break;
dbus_message_iter_get_basic(&value, &vpnhost);
vpn_provider_set_string(data->provider, key, vpnhost);
+ } else if (g_str_equal(key, "Username")) {
+ dbus_message_iter_next(&entry);
+ if (dbus_message_iter_get_arg_type(&entry)
+ != DBUS_TYPE_VARIANT)
+ break;
+ dbus_message_iter_recurse(&entry, &value);
+ if (dbus_message_iter_get_arg_type(&value)
+ != DBUS_TYPE_STRING)
+ break;
+ dbus_message_iter_get_basic(&value, &username);
+ vpn_provider_set_string_hide_value(data->provider,
+ "OpenConnect.Username", username);
+ } else if (g_str_equal(key, "Password")) {
+ dbus_message_iter_next(&entry);
+ if (dbus_message_iter_get_arg_type(&entry)
+ != DBUS_TYPE_VARIANT)
+ break;
+ dbus_message_iter_recurse(&entry, &value);
+ if (dbus_message_iter_get_arg_type(&value)
+ != DBUS_TYPE_STRING)
+ break;
+ dbus_message_iter_get_basic(&value, &password);
+ vpn_provider_set_string_hide_value(data->provider,
+ "OpenConnect.Password", password);
+ } else if (g_str_equal(key, "OpenConnect.PKCSPassword")) {
+ dbus_message_iter_next(&entry);
+ if (dbus_message_iter_get_arg_type(&entry)
+ != DBUS_TYPE_VARIANT)
+ break;
+ dbus_message_iter_recurse(&entry, &value);
+ if (dbus_message_iter_get_arg_type(&value)
+ != DBUS_TYPE_STRING)
+ break;
+ dbus_message_iter_get_basic(&value, &pkcspassword);
+ vpn_provider_set_string_hide_value(data->provider, key,
+ pkcspassword);
}
dbus_message_iter_next(&dict);
}
- if (!cookie || !servercert || !vpnhost)
- goto err;
-
- run_connect(data->provider, data->task, data->if_name, data->cb,
- data->user_data);
+ switch (data->connect_type) {
+ case OC_CONNECT_COOKIE:
+ if (!cookie)
+ goto err;
+
+ break;
+ case OC_CONNECT_USERPASS:
+ /* fall through */
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
+ if (!username || !password)
+ goto err;
+
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ break; // This should not be reached.
+ case OC_CONNECT_PKCS:
+ if (!pkcspassword)
+ goto err;
+
+ break;
+ }
- free_private_data(data);
+ err = run_connect(data);
+ if (err != -EINPROGRESS)
+ goto err;
return;
err:
- vpn_provider_indicate_error(data->provider,
- VPN_PROVIDER_ERROR_AUTH_FAILED);
+ oc_connect_done(data, EACCES);
out:
free_private_data(data);
}
-static int request_cookie_input(struct vpn_provider *provider,
- struct oc_private_data *data,
- const char *dbus_sender)
+static int request_input_credentials(struct oc_private_data *data)
{
DBusMessage *message;
- const char *path, *agent_sender, *agent_path;
+ const char *path, *agent_sender, *agent_path, *username;
DBusMessageIter iter;
DBusMessageIter dict;
- const char *str;
int err;
void *agent;
- agent = connman_agent_get_info(dbus_sender, &agent_sender,
- &agent_path);
- if (!provider || !agent || !agent_path)
+ if (!data)
+ return -ESRCH;
+
+ connman_info("provider %p", data->provider);
+
+ agent = connman_agent_get_info(data->dbus_sender,
+ &agent_sender, &agent_path);
+ if (!data->provider || !agent || !agent_path)
return -ESRCH;
message = dbus_message_new_method_call(agent_sender, agent_path,
@@ -426,120 +1081,244 @@ static int request_cookie_input(struct vpn_provider
*provider,
dbus_message_iter_init_append(message, &iter);
- path = vpn_provider_get_path(provider);
- dbus_message_iter_append_basic(&iter,
- DBUS_TYPE_OBJECT_PATH, &path);
+ path = vpn_provider_get_path(data->provider);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
connman_dbus_dict_open(&iter, &dict);
- str = vpn_provider_get_string(provider, "OpenConnect.CACert");
- if (str)
- connman_dbus_dict_append_dict(&dict, "OpenConnect.CACert",
+ request_input_append_to_dict(data->provider, &dict,
request_input_append_informational,
- (void *)str);
-
- str = vpn_provider_get_string(provider, "OpenConnect.ClientCert");
- if (str)
- connman_dbus_dict_append_dict(&dict, "OpenConnect.ClientCert",
+ "OpenConnect.CACert");
+
+ /*
+ * For backwards compatibility add OpenConnect.ServerCert and
+ * OpenConnect.VPNHost as madnatory only in the default authentication
+ * mode. Otherwise. add the fields as informational. These should be
+ * set in provider settings and not to be queried with every connection
+ * attempt.
+ */
+ request_input_append_to_dict(data->provider, &dict,
+ data->connect_type == OC_CONNECT_COOKIE ?
+ request_input_append_optional :
request_input_append_informational,
- (void *)str);
+ "OpenConnect.ServerCert");
- connman_dbus_dict_append_dict(&dict, "OpenConnect.ServerCert",
- request_input_append_mandatory, NULL);
-
- connman_dbus_dict_append_dict(&dict, "OpenConnect.VPNHost",
- request_input_append_mandatory, NULL);
+ request_input_append_to_dict(data->provider, &dict,
+ data->connect_type == OC_CONNECT_COOKIE ?
+ request_input_append_optional :
+ request_input_append_informational,
+ "OpenConnect.VPNHost");
+
+ if (vpn_provider_get_authentication_errors(data->provider))
+ vpn_agent_append_auth_failure(&dict, data->provider, NULL);
+
+ switch (data->connect_type) {
+ case OC_CONNECT_COOKIE:
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_mandatory,
+ "OpenConnect.Cookie");
+ break;
+ /*
+ * The authentication is done with username and password to get the
+ * cookie for connection.
+ */
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
+ /* fallthrough */
+ case OC_CONNECT_USERPASS:
+ username = vpn_provider_get_string(data->provider,
+ "OpenConnect.Username");
+ vpn_agent_append_user_info(&dict, data->provider, username);
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ return -EINVAL;
+ case OC_CONNECT_PKCS:
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_informational,
+ "OpenConnect.PKCSClientCert");
- connman_dbus_dict_append_dict(&dict, "OpenConnect.Cookie",
- request_input_append_mandatory, NULL);
+ request_input_append_to_dict(data->provider, &dict,
+ request_input_append_password,
+ "OpenConnect.PKCSPassword");
+ break;
+ }
- vpn_agent_append_host_and_name(&dict, provider);
+ vpn_agent_append_host_and_name(&dict, data->provider);
connman_dbus_dict_close(&iter, &dict);
- err = connman_agent_queue_message(provider, message,
+ err = connman_agent_queue_message(data->provider, message,
connman_timeout_input_request(),
- request_input_cookie_reply, data, agent);
+ request_input_credentials_reply, data, agent);
- if (err < 0 && err != -EBUSY) {
- DBG("error %d sending agent request", err);
- dbus_message_unref(message);
+ dbus_message_unref(message);
+ if (err < 0 && err != -EBUSY) {
+ connman_error("cannot send agent request, error: %d", err);
return err;
}
- dbus_message_unref(message);
-
return -EINPROGRESS;
}
+static enum oc_connect_type get_authentication_type(
+ struct vpn_provider *provider)
+{
+ const char *auth;
+ enum oc_connect_type type;
+
+ auth = vpn_provider_get_string(provider, "OpenConnect.AuthType");
+ if (!auth)
+ goto out;
+
+ for (type = 0; connect_types[type]; type++) {
+ if (!g_strcmp0(auth, connect_types[type])) {
+ connman_info("auth type %d/%s", type,
+ connect_types[type]);
+ return type;
+ }
+ }
+
+out:
+ /* Default to cookie */
+ return OC_CONNECT_COOKIE;
+}
+
static int oc_connect(struct vpn_provider *provider,
struct connman_task *task, const char *if_name,
vpn_provider_connect_cb_t cb,
const char *dbus_sender, void *user_data)
{
- const char *vpnhost, *vpncookie, *servercert;
+ struct oc_private_data *data;
+ const char *vpnhost, *vpncookie, *certificate, *username, *password;
+ const char *private_key;
int err;
+ connman_info("provider %p task %p", provider, task);
+
vpnhost = vpn_provider_get_string(provider, "Host");
if (!vpnhost) {
connman_error("Host not set; cannot enable VPN");
return -EINVAL;
}
- vpncookie = vpn_provider_get_string(provider, "OpenConnect.Cookie");
- servercert = vpn_provider_get_string(provider,
- "OpenConnect.ServerCert");
- if (!vpncookie || !servercert) {
- struct oc_private_data *data;
-
- data = g_try_new0(struct oc_private_data, 1);
- if (!data)
- return -ENOMEM;
-
- data->provider = provider;
- data->task = task;
- data->if_name = g_strdup(if_name);
- data->cb = cb;
- data->user_data = user_data;
-
- err = request_cookie_input(provider, data, dbus_sender);
- if (err != -EINPROGRESS) {
- vpn_provider_indicate_error(data->provider,
- VPN_PROVIDER_ERROR_LOGIN_FAILED);
+ data = g_try_new0(struct oc_private_data, 1);
+ if (!data)
+ return -ENOMEM;
+
+ vpn_provider_set_plugin_data(provider, data);
+ data->provider = vpn_provider_ref(provider);
+ data->task = task;
+ data->if_name = g_strdup(if_name);
+ data->dbus_sender = g_strdup(dbus_sender);
+ data->cb = cb;
+ data->user_data = user_data;
+ data->connect_type = get_authentication_type(provider);
+
+ switch (data->connect_type) {
+ case OC_CONNECT_COOKIE:
+ vpncookie = vpn_provider_get_string(provider,
+ "OpenConnect.Cookie");
+ if (!vpncookie || !g_strcmp0(vpncookie, "-"))
+ goto request_input;
+
+ break;
+ case OC_CONNECT_USERPASS:
+ username = vpn_provider_get_string(provider,
+ "OpenConnect.Username");
+ password = vpn_provider_get_string(provider,
+ "OpenConnect.Password");
+ if (!username || !password || !g_strcmp0(username, "-") ||
+ !g_strcmp0(password, "-"))
+ goto request_input;
+
+ break;
+ case OC_CONNECT_COOKIE_WITH_USERPASS:
+ vpncookie = vpn_provider_get_string(provider,
+ "OpenConnect.Cookie");
+ /* Username and password must be set if cookie is missing */
+ if (!vpncookie) {
+ username = vpn_provider_get_string(provider,
+ "OpenConnect.Username");
+ password = vpn_provider_get_string(provider,
+ "OpenConnect.Password");
+
+ if (!username || !password ||
+ !g_strcmp0(username, "-") ||
+ !g_strcmp0(password, "-"))
+ goto request_input;
+ } else if (!g_strcmp0(vpncookie, "-")) {
+ goto request_input;
+ }
+
+ break;
+ case OC_CONNECT_PUBLICKEY:
+ certificate = vpn_provider_get_string(provider,
+ "OpenConnect.ClientCert");
+ private_key = vpn_provider_get_string(provider,
+ "OpenConnect.UserPrivateKey");
+
+ if (!certificate || !private_key) {
+ connman_warn("missing certificate and/or private key");
+ oc_connect_done(data, EACCES);
free_private_data(data);
+ return -EACCES;
}
- return err;
+
+ break;
+ case OC_CONNECT_PKCS:
+ certificate = vpn_provider_get_string(provider,
+ "OpenConnect.PKCSClientCert");
+ if (certificate) {
+ password = vpn_provider_get_string(provider,
+ "OpenConnect.PKCSPassword");
+ if (!password || !g_strcmp0(password, "-"))
+ goto request_input;
+ } else {
+ DBG("missing PKCS certificate");
+ oc_connect_done(data, EACCES);
+ free_private_data(data);
+ return -EACCES;
+ }
+
+ break;
}
- return run_connect(provider, task, if_name, cb, user_data);
+ return run_connect(data);
+
+request_input:
+ err = request_input_credentials(data);
+ if (err != -EINPROGRESS) {
+ oc_connect_done(data, err);
+ vpn_provider_indicate_error(data->provider,
+ VPN_PROVIDER_ERROR_LOGIN_FAILED);
+ free_private_data(data);
+ }
+
+ return err;
+}
+
+static void oc_disconnect(struct vpn_provider *provider)
+{
+ connman_info("provider %p", provider);
+
+ if (!provider)
+ return;
+
+ /*
+ * OpenConnect may be disconnect by timeout in connmand before running
+ * the openconnect process. In such case it is important to cancel the
+ * agent request to avoid having multiple ones visible.
+ */
+ connman_agent_cancel(provider);
}
static int oc_save(struct vpn_provider *provider, GKeyFile *keyfile)
{
- const char *setting, *option;
+ const char *save_group;
+ const char *option;
int i;
- setting = vpn_provider_get_string(provider,
- "OpenConnect.ServerCert");
- if (setting)
- g_key_file_set_string(keyfile,
- vpn_provider_get_save_group(provider),
- "OpenConnect.ServerCert", setting);
-
- setting = vpn_provider_get_string(provider,
- "OpenConnect.CACert");
- if (setting)
- g_key_file_set_string(keyfile,
- vpn_provider_get_save_group(provider),
- "OpenConnect.CACert", setting);
-
- setting = vpn_provider_get_string(provider,
- "VPN.MTU");
- if (setting)
- g_key_file_set_string(keyfile,
- vpn_provider_get_save_group(provider),
- "VPN.MTU", setting);
+ save_group = vpn_provider_get_save_group(provider);
for (i = 0; i < (int)ARRAY_SIZE(oc_options); i++) {
if (strncmp(oc_options[i].cm_opt, "OpenConnect.", 12) == 0) {
@@ -548,8 +1327,7 @@ static int oc_save(struct vpn_provider *provider, GKeyFile
*keyfile)
if (!option)
continue;
- g_key_file_set_string(keyfile,
- vpn_provider_get_save_group(provider),
+ g_key_file_set_string(keyfile, save_group,
oc_options[i].cm_opt, option);
}
}
@@ -559,13 +1337,21 @@ static int oc_save(struct vpn_provider *provider,
GKeyFile *keyfile)
static int oc_error_code(struct vpn_provider *provider, int exit_code)
{
+ connman_info("%d", exit_code);
+
+ /* OpenConnect process return values are ambigious in definition
+ * https://github.com/openconnect/openconnect/blob/master/main.c#L1693
+ * and it is safer not to rely on them. Login error cannot be
+ * differentiated from connection errors, e.g., when self signed
+ * certificate is rejected by user setting.
+ */
switch (exit_code) {
- case 1:
case 2:
- vpn_provider_set_string_hide_value(provider,
- "OpenConnect.Cookie", NULL);
+ /* Cookie has failed */
+ clear_provider_credentials(provider);
return VPN_PROVIDER_ERROR_LOGIN_FAILED;
+ case 1:
default:
return VPN_PROVIDER_ERROR_UNKNOWN;
}
@@ -603,6 +1389,7 @@ static int oc_route_env_parse(struct vpn_provider
*provider, const char *key,
static struct vpn_driver vpn_driver = {
.notify = oc_notify,
.connect = oc_connect,
+ .disconnect = oc_disconnect,
.error_code = oc_error_code,
.save = oc_save,
.route_env_parse = oc_route_env_parse,
--
2.20.1
------------------------------
Subject: Digest Footer
_______________________________________________
connman mailing list -- [email protected]
To unsubscribe send an email to [email protected]
------------------------------
End of connman Digest, Vol 48, Issue 14
***************************************