Send connman mailing list submissions to
        [email protected]

To subscribe or unsubscribe via the World Wide Web, visit
        https://lists.01.org/mailman/listinfo/connman
or, 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/6] vpnc: Detect authentication and connection errors
      (Jussi Laakkonen)
   2. [PATCH v2 5/6] vpnc: Implement VPN agent support (Jussi Laakkonen)
   3. [PATCH v2 6/6] vpnc: Detect authentication and connection
      errors (Jussi Laakkonen)


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

Message: 1
Date: Tue, 28 May 2019 15:47:34 +0300
From: Jussi Laakkonen <[email protected]>
To: [email protected]
Subject: [PATCH 6/6] vpnc: Detect authentication and connection errors
Message-ID: <[email protected]>

Add support for detecting authentication and connection errors reported
by VPNC. These are read from stderr using GIOChannel.

In case of an authentication error VPN_PROVIDER_ERROR_AUTH_FAILED is
indicated. Authentication errors are:
 - hash comparison failed
 - authentication unsuccessful
 - expected xauth packet; rejected

In case of an connection error VPN_PROVIDER_ERROR_CONNECT is
indicated. Connect errors are:
 - unknown host
 - no response from target
 - receiving packet: No route to host

The approach is imitated from how OpenVPN (openvpn.c) reads output via
management channel.
---
 vpn/plugins/vpnc.c | 93 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 89 insertions(+), 4 deletions(-)

diff --git a/vpn/plugins/vpnc.c b/vpn/plugins/vpnc.c
index 7808c3d8..a05d70b6 100644
--- a/vpn/plugins/vpnc.c
+++ b/vpn/plugins/vpnc.c
@@ -92,6 +92,8 @@ struct vc_private_data {
        char *if_name;
        vpn_provider_connect_cb_t cb;
        void *user_data;
+       int err_ch_id;
+       GIOChannel *err_ch;
 };
 
 static void vc_connect_done(struct vc_private_data *data, int err)
@@ -109,6 +111,29 @@ static void vc_connect_done(struct vc_private_data *data, 
int err)
        }
 }
 
+static void close_io_channel(struct vc_private_data *data, GIOChannel *channel)
+{
+       if (!data || !channel)
+               return;
+
+       if (data->err_ch == channel) {
+               DBG("closing stderr");
+
+               if (data->err_ch_id) {
+                       g_source_remove(data->err_ch_id);
+                       data->err_ch_id = 0;
+               }
+
+               if (!data->err_ch)
+                       return;
+
+               g_io_channel_shutdown(data->err_ch, FALSE, NULL);
+               g_io_channel_unref(data->err_ch);
+
+               data->err_ch = NULL;
+       }
+}
+
 static void free_private_data(struct vc_private_data *data)
 {
        DBG("data %p", data);
@@ -351,6 +376,59 @@ static void vc_died(struct connman_task *task, int 
exit_code, void *user_data)
        free_private_data(data);
 }
 
+static gboolean io_channel_cb(GIOChannel *source, GIOCondition condition,
+                       gpointer user_data)
+{
+       struct vc_private_data *data;
+       const char *auth_failures[] = {
+                       VPNC ": hash comparison failed",
+                       VPNC ": authentication unsuccessful",
+                       VPNC ": expected xauth packet; rejected",
+                       NULL
+       };
+       const char *conn_failures[] = {
+                       VPNC ": unknown host",
+                       VPNC ": no response from target",
+                       VPNC ": receiving packet: No route to host",
+                       NULL
+       };
+       char *str;
+       int i;
+
+       data = user_data;
+
+       if ((condition & G_IO_IN) &&
+               g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
+                                                       G_IO_STATUS_NORMAL) {
+               str[strlen(str) - 1] = '\0';
+
+               for (i = 0; auth_failures[i]; i++) {
+                       if (g_str_has_prefix(str, auth_failures[i])) {
+                               DBG("authentication failed: %s", str);
+
+                               vpn_provider_indicate_error(data->provider,
+                                       VPN_PROVIDER_ERROR_AUTH_FAILED);
+                       }
+               }
+
+               for (i = 0; conn_failures[i]; i++) {
+                       if (g_str_has_prefix(str, conn_failures[i])) {
+                               DBG("connection failed: %s", str);
+
+                               vpn_provider_indicate_error(data->provider,
+                                       VPN_PROVIDER_ERROR_CONNECT_FAILED);
+                       }
+               }
+
+               g_free(str);
+       } else if (condition & (G_IO_ERR | G_IO_HUP)) {
+               DBG("Channel termination");
+               close_io_channel(data, source);
+       }
+
+       return TRUE;
+}
+
 static int run_connect(struct vc_private_data *data)
 {
        struct vpn_provider *provider;
@@ -360,7 +438,8 @@ static int run_connect(struct vc_private_data *data)
        const char *if_name;
        const char *option;
        int err;
-       int fd;
+       int fd_in;
+       int fd_err;
        int i;
 
        provider = data->provider;
@@ -392,14 +471,15 @@ static int run_connect(struct vc_private_data *data)
 
        connman_task_add_argument(task, "-", NULL);
 
-       err = connman_task_run(data->task, vc_died, data, &fd, NULL, NULL);
+       err = connman_task_run(data->task, vc_died, data, &fd_in, NULL,
+                               &fd_err);
        if (err < 0) {
                connman_error("vpnc failed to start");
                err = -EIO;
                goto done;
        }
 
-       err = vc_write_config_data(provider, fd);
+       err = vc_write_config_data(provider, fd_in);
 
        if (err) {
                DBG("config write error %s", strerror(err));
@@ -408,8 +488,13 @@ static int run_connect(struct vc_private_data *data)
 
        err = -EINPROGRESS;
 
+       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,
+                               (GIOFunc)io_channel_cb, data);
+
 done:
-       close(fd);
+       close(fd_in);
 
        /*
         * Clear out credentials if they are non-immutable. If this is called
-- 
2.20.1



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

Message: 2
Date: Tue, 28 May 2019 18:33:08 +0300
From: Jussi Laakkonen <[email protected]>
To: [email protected]
Subject: [PATCH v2 5/6] vpnc: Implement VPN agent support
Message-ID: <[email protected]>

This adds VPN agent support for VPNC plugin. IPSec.Secret,
Xauth.Username and Xauth.Password are queried from agent if not set in
.config of the VPN provider.

By default, VPNC does not save the credentials to provider config. The
VPNC credentials stored by vpn-provider.c in the provider settings
strings are cleared after run_connect() has finished.

The values of IPSec.Secret, Xauth.Username and Xauth.Password are set to
"-" in order to get them retrieved via VPN agent at next connect request.
The credentials read from .config file are not being reset as the
immutable value of them is checked first. This approach supports also
partially defined credentials in .config, leaving some of them to be
retrieved using a VPN agent. The immutable values are sent as
informational, passwords are changed to "********" to hide them since
the values cannot be changed.

The approach for setting the credentials to "-" follows the approach of
OpenVPN plugin, where credential set as "-" is retrieved over management
interface.

If the credential has something else than "-" set it is forwarded to VPN
agent as old information, if at least one of the credentials is missing.
In this case request_input_credentials() is called and a message is sent
to VPN agent if any. request_input_credentials_reply() handles the
message sent by VPN agent, or errors (Error.Canceled = ECONNABORTED and
Error.Timeout = ETIMEDOUT) from VPN_AGENT_INTERFACE. vc_connect_done()
is invoked to call the callback function, which is executed only once,
with the error code, if any. In case of success, run_connect() is called
and VPN is attempted to connect.

When vc_notify() is called, it retrieves the plugin data from provider
and utilizes its callback by calling vc_connect_done() with the proper
error code, 0 being success.

If plugin dies or is disconnected, vc_died() handles the shutdown by
canceling all agent requests and calls vpn_died(). Last, the data
allocated for the connection is free'd.
---
Changes since V2:
 * Remove testing debug messages that reveal secret and password.
 * Set to send immutable values as informational to VPN agent. Also
   hide immutable passwords as "********" when sending to VPN agent as
   informational.

 vpn/plugins/vpnc.c | 463 ++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 433 insertions(+), 30 deletions(-)

diff --git a/vpn/plugins/vpnc.c b/vpn/plugins/vpnc.c
index 87a746cc..05eeb283 100644
--- a/vpn/plugins/vpnc.c
+++ b/vpn/plugins/vpnc.c
@@ -39,15 +39,18 @@
 #include <connman/task.h>
 #include <connman/ipconfig.h>
 #include <connman/dbus.h>
+#include <connman/agent.h>
+#include <connman/setting.h>
+#include <connman/vpn-dbus.h>
 
 #include "../vpn-provider.h"
+#include "../vpn-agent.h"
 
 #include "vpn.h"
+#include "../vpn.h"
 
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
 
-static DBusConnection *connection;
-
 enum {
        OPT_STRING = 1,
        OPT_BOOLEAN = 2,
@@ -83,14 +86,58 @@ struct {
                                                                        true },
 };
 
+struct vc_private_data {
+       struct vpn_provider *provider;
+       struct connman_task *task;
+       char *if_name;
+       vpn_provider_connect_cb_t cb;
+       void *user_data;
+};
+
+static void vc_connect_done(struct vc_private_data *data, int err)
+{
+       DBG("data %p err %d", data, 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 free_private_data(struct vc_private_data *data)
+{
+       DBG("data %p", data);
+
+       if (!data || !data->provider)
+               return;
+
+       DBG("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);
+
+       g_free(data->if_name);
+       g_free(data);
+}
+
 static int vc_notify(DBusMessage *msg, struct vpn_provider *provider)
 {
        DBusMessageIter iter, dict;
        char *address = NULL, *netmask = NULL, *gateway = NULL;
        struct connman_ipaddress *ipaddress;
        const char *reason, *key, *value;
+       struct vc_private_data *data;
        int type;
 
+       data = vpn_provider_get_plugin_data(provider);
+
        dbus_message_iter_init(msg, &iter);
 
        type = dbus_message_iter_get_arg_type(&iter);
@@ -104,11 +151,14 @@ static int vc_notify(DBusMessage *msg, struct 
vpn_provider *provider)
 
        if (!provider) {
                connman_error("No provider found");
+               vc_connect_done(data, ENOENT);
                return VPN_STATE_FAILURE;
        }
 
-       if (strcmp(reason, "connect"))
+       if (g_strcmp0(reason, "connect")) {
+               vc_connect_done(data, EIO);
                return VPN_STATE_DISCONNECT;
+       }
 
        dbus_message_iter_recurse(&iter, &dict);
 
@@ -160,7 +210,7 @@ static int vc_notify(DBusMessage *msg, struct vpn_provider 
*provider)
                g_free(address);
                g_free(netmask);
                g_free(gateway);
-
+               vc_connect_done(data, EIO);
                return VPN_STATE_FAILURE;
        }
 
@@ -172,6 +222,7 @@ static int vc_notify(DBusMessage *msg, struct vpn_provider 
*provider)
        g_free(gateway);
        connman_ipaddress_free(ipaddress);
 
+       vc_connect_done(data, 0);
        return VPN_STATE_CONNECT;
 }
 
@@ -280,27 +331,45 @@ static int vc_save(struct vpn_provider *provider, 
GKeyFile *keyfile)
        return 0;
 }
 
-static int vc_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)
+static void vc_died(struct connman_task *task, int exit_code, void *user_data)
 {
-       const char *option;
-       int err = 0, fd;
+       struct vc_private_data *data = user_data;
 
-       option = vpn_provider_get_string(provider, "Host");
-       if (!option) {
-               connman_error("Host not set; cannot enable VPN");
-               err = -EINVAL;
-               goto done;
-       }
-       option = vpn_provider_get_string(provider, "VPNC.IPSec.ID");
-       if (!option) {
-               connman_error("Group not set; cannot enable VPN");
-               err = -EINVAL;
-               goto done;
+       DBG("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 int run_connect(struct vc_private_data *data)
+{
+       struct vpn_provider *provider;
+       struct connman_task *task;
+       const char *credentials[] = {"VPNC.IPSec.Secret", "VPNC.Xauth.Username",
+                               "VPNC.Xauth.Password", NULL};
+       const char *if_name;
+       const char *option;
+       int err;
+       int fd;
+       int i;
+
+       provider = data->provider;
+       task = data->task;
+       if_name = data->if_name;
+
+       DBG("provider %p task %p interface %s user_data %p", provider, task,
+                               if_name, data->user_data);
+
        connman_task_add_argument(task, "--non-inter", NULL);
        connman_task_add_argument(task, "--no-detach", NULL);
 
@@ -323,8 +392,7 @@ static int vc_connect(struct vpn_provider *provider,
 
        connman_task_add_argument(task, "-", NULL);
 
-       err = connman_task_run(task, vpn_died, provider,
-                               &fd, NULL, NULL);
+       err = connman_task_run(data->task, vc_died, data, &fd, NULL, NULL);
        if (err < 0) {
                connman_error("vpnc failed to start");
                err = -EIO;
@@ -333,15 +401,354 @@ static int vc_connect(struct vpn_provider *provider,
 
        err = vc_write_config_data(provider, fd);
 
-       close(fd);
+       if (err) {
+               DBG("config write error %s", strerror(err));
+               goto done;
+       }
+
+       err = -EINPROGRESS;
 
 done:
-       if (cb)
-               cb(provider, user_data, err);
+       close(fd);
+
+       /*
+        * Clear out credentials if they are non-immutable. If this is called
+        * directly from vc_connect() all credentials are read from config and
+        * are set as immutable, so no change is done. In case a VPN agent is
+        * used these values should be reset to "-" in order to retrieve them
+        * from VPN agent next time VPN connection is established. This supports
+        * then partially defined credentials in .config and some can be
+        * retrieved using an agent.
+        */
+       for (i = 0; credentials[i]; i++) {
+               const char *key = credentials[i];
+               if (!vpn_provider_get_string_immutable(provider, key))
+                       vpn_provider_set_string(provider, key, "-");
+       }
 
        return err;
 }
 
+static void request_input_append_mandatory(DBusMessageIter *iter,
+               void *user_data)
+{
+       char *str = "string";
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+       str = "mandatory";
+       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_password(DBusMessageIter *iter,
+               void *user_data)
+{
+       char *str = "password";
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &str);
+       str = "mandatory";
+       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)
+{
+       char *str = "password";
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               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);
+}
+
+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);
+
+       /* 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, (void *)str);
+}
+
+static void request_input_credentials_reply(DBusMessage *reply, void 
*user_data)
+{
+       struct vc_private_data *data = user_data;
+       char *secret = NULL, *username = NULL, *password = NULL;
+       const char *key;
+       DBusMessageIter iter, dict;
+       DBusError error;
+       int err_int = 0;
+
+       DBG("provider %p", data->provider);
+
+       dbus_error_init(&error);
+
+       if (dbus_set_error_from_message(&error, reply)) {
+               if (!g_strcmp0(error.name, VPN_AGENT_INTERFACE
+                                                       ".Error.Canceled"))
+                       err_int = ECONNABORTED;
+
+               if (!g_strcmp0(error.name, VPN_AGENT_INTERFACE
+                                                       ".Error.Timeout"))
+                       err_int = ETIMEDOUT;
+
+               dbus_error_free(&error);
+               goto abort;
+       }
+
+       if (!vpn_agent_check_reply_has_dict(reply))
+               goto err;
+
+       dbus_message_iter_init(reply, &iter);
+       dbus_message_iter_recurse(&iter, &dict);
+       while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
+               DBusMessageIter entry, value;
+
+               dbus_message_iter_recurse(&dict, &entry);
+               if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
+                       break;
+
+               dbus_message_iter_get_basic(&entry, &key);
+
+               if (g_str_equal(key, "VPNC.IPSec.Secret")) {
+                       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, &secret);
+                       vpn_provider_set_string_hide_value(data->provider,
+                                       key, secret);
+
+               } else if (g_str_equal(key, "VPNC.Xauth.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(data->provider, key, username);
+
+               } else if (g_str_equal(key, "VPNC.Xauth.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, key,
+                                               password);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (!secret || !username || !password)
+               goto err;
+
+       err_int = run_connect(data);
+       if (err_int != -EINPROGRESS)
+               goto err;
+
+       return;
+
+err:
+       err_int = EACCES;
+
+abort:
+       vc_connect_done(data, err_int);
+
+       switch (err_int) {
+       case EACCES:
+               vpn_provider_indicate_error(data->provider,
+                                       VPN_PROVIDER_ERROR_AUTH_FAILED);
+               break;
+       case ECONNABORTED:
+       case ETIMEDOUT:
+               vpn_provider_indicate_error(data->provider,
+                                       VPN_PROVIDER_ERROR_UNKNOWN);
+       }
+}
+
+static int request_input_credentials(struct vc_private_data *data,
+                                       const char* dbus_sender)
+{
+       DBusMessage *message;
+       const char *path, *agent_sender, *agent_path;
+       DBusMessageIter iter;
+       DBusMessageIter dict;
+       int err;
+       void *agent;
+
+       if (!data || !data->provider)
+               return -ENOENT;
+
+       DBG("data %p provider %p sender %s", data, data->provider, dbus_sender);
+
+       agent = connman_agent_get_info(dbus_sender, &agent_sender, &agent_path);
+       if (!agent || !agent_path)
+               return -ESRCH;
+
+       message = dbus_message_new_method_call(agent_sender, agent_path,
+                                       VPN_AGENT_INTERFACE,
+                                       "RequestInput");
+       if (!message)
+               return -ENOMEM;
+
+       dbus_message_iter_init_append(message, &iter);
+
+       path = vpn_provider_get_path(data->provider);
+       dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+       connman_dbus_dict_open(&iter, &dict);
+
+       request_input_append_to_dict(data->provider, &dict,
+                               request_input_append_password,
+                               "VPNC.IPSec.Secret");
+       request_input_append_to_dict(data->provider, &dict,
+                               request_input_append_mandatory,
+                               "VPNC.Xauth.Username");
+       request_input_append_to_dict(data->provider, &dict,
+                               request_input_append_password,
+                               "VPNC.Xauth.Password");
+
+       vpn_agent_append_host_and_name(&dict, data->provider);
+
+       connman_dbus_dict_close(&iter, &dict);
+
+       err = connman_agent_queue_message(data->provider, message,
+                               connman_timeout_input_request(),
+                               request_input_credentials_reply, data, agent);
+
+       if (err < 0 && err != -EBUSY) {
+               DBG("error %d sending agent request", err);
+               dbus_message_unref(message);
+
+               return err;
+       }
+
+       dbus_message_unref(message);
+
+       return -EINPROGRESS;
+}
+
+static int vc_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)
+{
+       struct vc_private_data *data;
+       const char *option;
+       bool username_set = false;
+       bool password_set = false;
+       bool ipsec_secret_set = false;
+       int err;
+
+       DBG("provider %p if_name %s user_data %p", provider, if_name, 
user_data);
+
+       option = vpn_provider_get_string(provider, "Host");
+       if (!option) {
+               connman_error("Host not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       option = vpn_provider_get_string(provider, "VPNC.IPSec.ID");
+       if (!option) {
+               connman_error("Group not set; cannot enable VPN");
+               return -EINVAL;
+       }
+
+       option = vpn_provider_get_string(provider, "VPNC.IPSec.Secret");
+       if (option && *option && g_strcmp0(option, "-"))
+               ipsec_secret_set = true;
+
+       option = vpn_provider_get_string(provider, "VPNC.Xauth.Username");
+       if (option && *option && g_strcmp0(option, "-"))
+               username_set = true;
+
+       option = vpn_provider_get_string(provider, "VPNC.Xauth.Password");
+       if (option && *option && g_strcmp0(option, "-"))
+               password_set = true;
+
+       data = g_try_new0(struct vc_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->cb = cb;
+       data->user_data = user_data;
+
+       if (!ipsec_secret_set || !username_set || !password_set) {
+               err = request_input_credentials(data, dbus_sender);
+               if (err != -EINPROGRESS) {
+                       vc_connect_done(data, ECONNABORTED);
+                       vpn_provider_indicate_error(data->provider,
+                                       VPN_PROVIDER_ERROR_LOGIN_FAILED);
+                       free_private_data(data);
+               }
+
+               return err;
+       }
+
+       return run_connect(data);
+}
+
 static int vc_error_code(struct vpn_provider *provider, int exit_code)
 {
        switch (exit_code) {
@@ -384,16 +791,12 @@ static struct vpn_driver vpn_driver = {
 
 static int vpnc_init(void)
 {
-       connection = connman_dbus_get_connection();
-
        return vpn_register("vpnc", &vpn_driver, VPNC);
 }
 
 static void vpnc_exit(void)
 {
        vpn_unregister("vpnc");
-
-       dbus_connection_unref(connection);
 }
 
 CONNMAN_PLUGIN_DEFINE(vpnc, "vpnc plugin", VERSION,
-- 
2.20.1



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

Message: 3
Date: Tue, 28 May 2019 18:33:09 +0300
From: Jussi Laakkonen <[email protected]>
To: [email protected]
Subject: [PATCH v2 6/6] vpnc: Detect authentication and connection
        errors
Message-ID: <[email protected]>

Add support for detecting authentication and connection errors reported
by VPNC. These are read from stderr using GIOChannel.

In case of an authentication error VPN_PROVIDER_ERROR_AUTH_FAILED is
indicated. Authentication errors are:
 - hash comparison failed
 - authentication unsuccessful
 - expected xauth packet; rejected

In case of an connection error VPN_PROVIDER_ERROR_CONNECT is
indicated. Connect errors are:
 - unknown host
 - no response from target
 - receiving packet: No route to host

The approach is imitated from how OpenVPN (openvpn.c) reads output via
management channel.
---
Changes since V2:
 * Remove GIOChannel watch function if channel is closed. Use
   G_SOURCE_CONTINUE and G_SOURCE_REMOVE macros as return values.

 vpn/plugins/vpnc.c | 94 ++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 90 insertions(+), 4 deletions(-)

diff --git a/vpn/plugins/vpnc.c b/vpn/plugins/vpnc.c
index 05eeb283..e6c12bf2 100644
--- a/vpn/plugins/vpnc.c
+++ b/vpn/plugins/vpnc.c
@@ -92,6 +92,8 @@ struct vc_private_data {
        char *if_name;
        vpn_provider_connect_cb_t cb;
        void *user_data;
+       int err_ch_id;
+       GIOChannel *err_ch;
 };
 
 static void vc_connect_done(struct vc_private_data *data, int err)
@@ -109,6 +111,29 @@ static void vc_connect_done(struct vc_private_data *data, 
int err)
        }
 }
 
+static void close_io_channel(struct vc_private_data *data, GIOChannel *channel)
+{
+       if (!data || !channel)
+               return;
+
+       if (data->err_ch == channel) {
+               DBG("closing stderr");
+
+               if (data->err_ch_id) {
+                       g_source_remove(data->err_ch_id);
+                       data->err_ch_id = 0;
+               }
+
+               if (!data->err_ch)
+                       return;
+
+               g_io_channel_shutdown(data->err_ch, FALSE, NULL);
+               g_io_channel_unref(data->err_ch);
+
+               data->err_ch = NULL;
+       }
+}
+
 static void free_private_data(struct vc_private_data *data)
 {
        DBG("data %p", data);
@@ -351,6 +376,60 @@ static void vc_died(struct connman_task *task, int 
exit_code, void *user_data)
        free_private_data(data);
 }
 
+static gboolean io_channel_cb(GIOChannel *source, GIOCondition condition,
+                       gpointer user_data)
+{
+       struct vc_private_data *data;
+       const char *auth_failures[] = {
+                       VPNC ": hash comparison failed",
+                       VPNC ": authentication unsuccessful",
+                       VPNC ": expected xauth packet; rejected",
+                       NULL
+       };
+       const char *conn_failures[] = {
+                       VPNC ": unknown host",
+                       VPNC ": no response from target",
+                       VPNC ": receiving packet: No route to host",
+                       NULL
+       };
+       char *str;
+       int i;
+
+       data = user_data;
+
+       if ((condition & G_IO_IN) &&
+               g_io_channel_read_line(source, &str, NULL, NULL, NULL) ==
+                                                       G_IO_STATUS_NORMAL) {
+               str[strlen(str) - 1] = '\0';
+
+               for (i = 0; auth_failures[i]; i++) {
+                       if (g_str_has_prefix(str, auth_failures[i])) {
+                               DBG("authentication failed: %s", str);
+
+                               vpn_provider_indicate_error(data->provider,
+                                       VPN_PROVIDER_ERROR_AUTH_FAILED);
+                       }
+               }
+
+               for (i = 0; conn_failures[i]; i++) {
+                       if (g_str_has_prefix(str, conn_failures[i])) {
+                               DBG("connection failed: %s", str);
+
+                               vpn_provider_indicate_error(data->provider,
+                                       VPN_PROVIDER_ERROR_CONNECT_FAILED);
+                       }
+               }
+
+               g_free(str);
+       } else if (condition & (G_IO_ERR | G_IO_HUP)) {
+               DBG("Channel termination");
+               close_io_channel(data, source);
+               return G_SOURCE_REMOVE;
+       }
+
+       return G_SOURCE_CONTINUE;
+}
+
 static int run_connect(struct vc_private_data *data)
 {
        struct vpn_provider *provider;
@@ -360,7 +439,8 @@ static int run_connect(struct vc_private_data *data)
        const char *if_name;
        const char *option;
        int err;
-       int fd;
+       int fd_in;
+       int fd_err;
        int i;
 
        provider = data->provider;
@@ -392,14 +472,15 @@ static int run_connect(struct vc_private_data *data)
 
        connman_task_add_argument(task, "-", NULL);
 
-       err = connman_task_run(data->task, vc_died, data, &fd, NULL, NULL);
+       err = connman_task_run(data->task, vc_died, data, &fd_in, NULL,
+                               &fd_err);
        if (err < 0) {
                connman_error("vpnc failed to start");
                err = -EIO;
                goto done;
        }
 
-       err = vc_write_config_data(provider, fd);
+       err = vc_write_config_data(provider, fd_in);
 
        if (err) {
                DBG("config write error %s", strerror(err));
@@ -408,8 +489,13 @@ static int run_connect(struct vc_private_data *data)
 
        err = -EINPROGRESS;
 
+       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,
+                               (GIOFunc)io_channel_cb, data);
+
 done:
-       close(fd);
+       close(fd_in);
 
        /*
         * Clear out credentials if they are non-immutable. If this is called
-- 
2.20.1



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

Subject: Digest Footer

_______________________________________________
connman mailing list
[email protected]
https://lists.01.org/mailman/listinfo/connman


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

End of connman Digest, Vol 43, Issue 23
***************************************

Reply via email to