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. [PATCH 6/7] doc: Add new OpenConnect PKCS#12 parameters to VPN agent API
      (Jussi Laakkonen)
   2. [PATCH 5/7] openconnect: Rewrite plugin to support more auth methods and 
protocols
      (Jussi Laakkonen)


----------------------------------------------------------------------

Date: Wed,  2 Oct 2019 17:16:48 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH 6/7] doc: Add new OpenConnect PKCS#12 parameters to
        VPN agent API
To: [email protected]
Message-ID: <[email protected]>

Add new OpenConnect VPN agent parameters to the API documentation.
Following are added:
 Name                           Requirement     OC authentication type
 OpenConnect.PKCS12ClientCert   informational   pkcs12
 OpenConnect.PKCS12Password     mandatory       pkcs12
---
 doc/vpn-agent-api.txt | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/doc/vpn-agent-api.txt b/doc/vpn-agent-api.txt
index 26c732d1..fcb0e9e1 100644
--- a/doc/vpn-agent-api.txt
+++ b/doc/vpn-agent-api.txt
@@ -85,6 +85,15 @@ Fields               string Username
                        Return the OpenConnect cookie value that is used for
                        authenticating the VPN session.
 
+               string OpenConnect.PKCS12ClientCert
+
+                       Informational field containing a PKCS#12 URL or a path
+                       name for the PKCS#12 client certificate.
+
+               string OpenConnect.PKCS12Password
+
+                       Password for decrypting PKCS#12 client certificate.
+
                string OpenConnect.ServerCert
 
                        Return the OpenConnect server hash used to identify
-- 
2.20.1

------------------------------

Date: Wed,  2 Oct 2019 17:16:47 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH 5/7] 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 requests and
improve detection of errors from OpenConnect both stdout and stderr
outputs are 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.PKCS12ClientCert           string (path)   Provider
 OpenConnect.PKCS12Password             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#12 file:                            "pkcs12"

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.

With "pkcs12" the password is queried via agent for the PKCS#12 file.
This is implemented as separate from the public key authentication as
the separation of the actions required for the authentication are more
clear this way. PKCS#12 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#12
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 PKCS12Password are to be retrieved via VPN agent.
These are saved as hidden provider values and are not stored to disk.

ClientCert, UserPrivateKey and PKCS12ClienCert 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" or "no"). 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.

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
---
 vpn/plugins/openconnect.c | 1086 ++++++++++++++++++++++++++++++++-----
 1 file changed, 938 insertions(+), 148 deletions(-)

diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c
index 21224c4a..0e86fc9a 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
@@ -49,23 +50,117 @@
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
 
 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;
 } oc_options[] = {
-       { "OpenConnect.NoCertCheck", "--no-cert-check", 0 },
+       /* --no-cert-check is disabled in openconnect 8.02 */
+       { "OpenConnect.NoCertCheck", "--no-cert-check", 0, 0},
+       { "OpenConnect.ServerCert", "--servercert", 1, 1},
+       { "VPN.MTU", "--base-mtu", 1, 1 },
+       { "OpenConnect.Protocol", "--protocol", 1, 1 },
 };
 
+enum oc_connect_type {
+       OC_CONNECT_COOKIE = 0,
+       OC_CONNECT_COOKIE_WITH_USERPASS,
+       OC_CONNECT_USERPASS,
+       OC_CONNECT_PUBLICKEY,
+       OC_CONNECT_PKCS12,
+};
+
+static const char *connect_types[] = {"cookie", "cookie_with_userpass",
+                       "userpass", "publickey", "pkcs12", 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 +168,40 @@ 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;
+
+                       /* 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 +221,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 +234,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,107 +338,568 @@ 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)
+{
+       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;
+       }
+
+       return len;
+}
+
+static ssize_t write_data(int fd, const char *data)
 {
-       const char *vpnhost, *vpncookie, *servercert, *mtu;
-       int fd, err = 0, len;
+       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);
+       }
+
+       free_private_data(data);
+}
+
+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;
+}
+
+static bool strv_contains_prefix(const char *strv[], const char *str)
+{
+       int i;
+
+       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.PKCS12Password",
+                               "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",
+                               /* 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",
+                               "Failed to obtain WebVPN cookie",
+                               NULL
+       };
+       const char *pkcs12_failure =
+                               "Failed to decrypt PKCS#12 certificate file";
+       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, pkcs12_failure)) {
+                       connman_warn("invalid PKCS#12 password: %s", str);
+                       /*
+                        * Close IO channel to avoid deadlock. This is because
+                        * after PKCS#12 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 *usergroup;
+       const char *certificate = NULL;
+       const char *private_key;
+       bool setting;
+       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);
+               }
+
+               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_PKCS12:
+               certificate = vpn_provider_get_string(provider,
+                                       "OpenConnect.PKCS12ClientCert");
+               password = vpn_provider_get_string(data->provider,
+                                       "OpenConnect.PKCS12Password");
+               if (!certificate || !password) {
+                       err = -EACCES;
+                       goto done;
+               }
+
+               connman_task_add_argument(task, "--certificate", certificate);
+               connman_task_add_argument(task, "--passwd-on-stdin", NULL);
+               break;
+       }
+
+       usergroup = vpn_provider_get_string(provider, "OpenConnect.Usergroup");
+       if (usergroup)
+               connman_task_add_argument(task, "--usergroup", usergroup);
 
        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");
-
-       if (!vpncookie || !servercert) {
-               err = -EINVAL;
-               goto done;
-       }
 
        task_append_config_data(provider, task);
 
-       connman_task_add_argument(task, "--servercert", servercert);
+       setting = vpn_provider_get_boolean(provider, "OpenConnect.DisableIPv6",
+                               false);
+       if (setting)
+               connman_task_add_argument(task, "--disable-ipv6", NULL);
 
-       mtu = vpn_provider_get_string(provider, "VPN.MTU");
+       setting = vpn_provider_get_boolean(provider, "OpenConnect.NoDTLS",
+                               false);
+       if (setting)
+               connman_task_add_argument(task, "--no-dtls", NULL);
+
+       setting = vpn_provider_get_boolean(provider,
+                               "OpenConnect.NoHTTPKeepalive", false);
+       if (setting)
+               connman_task_add_argument(task, "--no-http-keepalive", NULL);
 
-       if (mtu)
-               connman_task_add_argument(task, "--mtu", (char *)mtu);
+       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, &fd_out,
+                               &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_PKCS12:
+               if (write_data(data->fd_in, password) != 0) {
+                       connman_error("openconnect failed to take PKCS#12 "
+                                               "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;
+
+       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, const char *str_type, void *user_data)
 {
-       const char *str;
-
-       str = "string";
-       connman_dbus_dict_append_basic(iter, "Type", DBUS_TYPE_STRING, &str);
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &str_type);
 
-       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);
 }
 
+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, *pkcs12password = NULL;
+       const char *key;
        DBusMessageIter iter, dict;
        int err;
 
-       DBG("provider %p", data->provider);
-
        if (!reply)
                goto err;
 
@@ -353,7 +938,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 +962,97 @@ 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.PKCS12Password")) {
+                       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, &pkcs12password);
+                       vpn_provider_set_string_hide_value(data->provider, key,
+                                               pkcs12password);
                }
 
                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_PKCS12:
+               if (!pkcs12password)
+                       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;
+
+       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 +1063,264 @@ 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);
-
-       connman_dbus_dict_append_dict(&dict, "OpenConnect.ServerCert",
-                       request_input_append_mandatory, NULL);
+                               "OpenConnect.ServerCert");
 
-       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_PKCS12:
+               request_input_append_to_dict(data->provider, &dict,
+                               request_input_append_informational,
+                               "OpenConnect.PKCS12ClientCert");
 
-       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.PKCS12Password");
+               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)
+                       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_PKCS12:
+               certificate = vpn_provider_get_string(provider,
+                               "OpenConnect.PKCS12ClientCert");
+               if (certificate) {
+                       password = vpn_provider_get_string(provider,
+                               "OpenConnect.PKCS12Password");
+                       if (!password || !g_strcmp0(password, "-"))
+                               goto request_input;
+               } else {
+                       connman_warn("missing PKCS#12 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 *setting_keys[] = {"OpenConnect.AllowSelfSignedCert",
+                               "OpenConnect.AuthType",
+                               "OpenConnect.CACert",
+                               "OpenConnect.ClientCert",
+                               "OpenConnect.PKCS12ClientCert",
+                               "OpenConnect.ServerCert",
+                               "OpenConnect.UserPrivateKey",
+                               "OpenConnect.DisableIPv6",
+                               "OpenConnect.NoHTTPKeepalive",
+                               "OpenConnect.NoDTLS",
+                               "VPN.MTU",
+                               NULL,
+       };
+       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);
+       save_group = vpn_provider_get_save_group(provider);
 
-       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);
+       for (i = 0; setting_keys[i]; i++) {
+               option = vpn_provider_get_string(provider, setting_keys[i]);
+               if (option)
+                       g_key_file_set_string(keyfile, save_group,
+                                               setting_keys[i], option);
+       }
 
        for (i = 0; i < (int)ARRAY_SIZE(oc_options); i++) {
                if (strncmp(oc_options[i].cm_opt, "OpenConnect.", 12) == 0) {
@@ -559,13 +1340,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 +1392,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 3
**************************************

Reply via email to