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 v2 3/9] doc: Add VpnAgent.AuthFailure to VPN agent API 
documentation
      (Jussi Laakkonen)
   2. [PATCH v2 4/9] vpn-provider: Implement setting string to bool conversion 
function
      (Jussi Laakkonen)
   3. [PATCH v2 2/9] vpn-agent: Implement function to add auth failures to VPN 
agent msg
      (Jussi Laakkonen)
   4. [PATCH v5 0/9] Rewrite OpenConnect plugin and enhance support for VPN 
auth errors
      (Jussi Laakkonen)
   5. [PATCH v3 1/9] vpn-provider: Implement simple connection and auth error 
counters
      (Jussi Laakkonen)
   6. [PATCH 9/9] TODO: Add task for libopenconnect use in OpenConnect VPN 
plugin
      (Jussi Laakkonen)
   7. [PATCH v5 6/9] openconnect: Use interactive mode when input to stdin is 
required
      (Jussi Laakkonen)


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

Date: Fri, 11 Oct 2019 15:52:53 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH v2 3/9] doc: Add VpnAgent.AuthFailure to VPN agent API
        documentation
To: [email protected]
Message-ID: <[email protected]>

Document new informational string field VpnAgent.Authfailure.
---

Changes since V2:
 * Just a bump for clarity.

 doc/vpn-agent-api.txt | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/doc/vpn-agent-api.txt b/doc/vpn-agent-api.txt
index 72bee9db..26c732d1 100644
--- a/doc/vpn-agent-api.txt
+++ b/doc/vpn-agent-api.txt
@@ -96,6 +96,13 @@ Fields               string Username
                        Return the final VPN server to use after possible
                        web authentication logins, selections and redirections.
 
+               string VpnAgent.AuthFailure
+
+                       Informational field that can be used to indicate VPN
+                       agent that previous authentication has failed and new
+                       credentials should be requested from user. Additional
+                       information about the failure can be added as "Value".
+
 Arguments      string Type
 
                        Contains the type of a field. For example "password",
-- 
2.20.1

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

Date: Fri, 11 Oct 2019 15:52:54 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH v2 4/9] vpn-provider: Implement setting string to bool
        conversion function
To: [email protected]
Message-ID: <[email protected]>

Add vpn_provider_get_boolean() to do conversion for saved setting
strings expected to be "true" or "false" in value. Given default value
is returned in case of invalid or missing string.
---

Changes since V2:
 * Just a bump for clarity.

 vpn/vpn-provider.c | 20 ++++++++++++++++++++
 vpn/vpn-provider.h |  2 ++
 2 files changed, 22 insertions(+)

diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c
index c368d26c..b8b94cbf 100644
--- a/vpn/vpn-provider.c
+++ b/vpn/vpn-provider.c
@@ -2368,6 +2368,26 @@ const char *vpn_provider_get_string(struct vpn_provider 
*provider,
        return setting->value;
 }
 
+bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
+                                                       bool default_value)
+{
+       struct vpn_setting *setting;
+
+       connman_info("provider %p key %s", provider, key);
+
+       setting = g_hash_table_lookup(provider->setting_strings, key);
+       if (!setting || !setting->value)
+               return default_value;
+
+       if (!g_strcmp0(setting->value, "true"))
+               return true;
+
+       if (!g_strcmp0(setting->value, "false"))
+               return false;
+
+       return default_value;
+}
+
 bool vpn_provider_get_string_immutable(struct vpn_provider *provider,
                                                        const char *key)
 {
diff --git a/vpn/vpn-provider.h b/vpn/vpn-provider.h
index 0838b2e5..36e16c35 100644
--- a/vpn/vpn-provider.h
+++ b/vpn/vpn-provider.h
@@ -88,6 +88,8 @@ const char *vpn_provider_get_string(struct vpn_provider 
*provider,
                                                        const char *key);
 bool vpn_provider_get_string_immutable(struct vpn_provider *provider,
                                                        const char *key);
+bool vpn_provider_get_boolean(struct vpn_provider *provider, const char *key,
+                                                       bool default_value);
 
 int vpn_provider_set_state(struct vpn_provider *provider,
                                        enum vpn_provider_state state);
-- 
2.20.1

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

Date: Fri, 11 Oct 2019 15:52:52 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH v2 2/9] vpn-agent: Implement function to add auth
        failures to VPN agent msg
To: [email protected]
Message-ID: <[email protected]>

A new field for VPN Agent API, "VpnAgent.AuthFailure" is introduced with
this change and a function to set the error with optional additional
information about failure is added. This field is to be used for
indicating VPN agent that authentication credentials should be cleared
if they are saved in order to request them again from the user.

If the given addtional information is not set, VPN provider settings is
queried using the key "VpnAgent.AuthFailure". The value should be run
time only and never to be stored within provider settings file.
---

Changes since V2:
 * Rename struct auth_failure_data to struct failure_data to be more generic.
 * Add copyright.

 vpn/vpn-agent.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++
 vpn/vpn-agent.h |  3 +++
 2 files changed, 57 insertions(+)

diff --git a/vpn/vpn-agent.c b/vpn/vpn-agent.c
index be9774a2..aa73ec54 100644
--- a/vpn/vpn-agent.c
+++ b/vpn/vpn-agent.c
@@ -3,6 +3,7 @@
  *  Connection Manager
  *
  *  Copyright (C) 2012  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
@@ -146,6 +147,59 @@ void vpn_agent_append_user_info(DBusMessageIter *iter,
                                &data);
 }
 
+struct failure_data {
+       struct vpn_provider *provider;
+       const char* type_str;
+       const char *key;
+       const char* str;
+};
+
+static void request_input_append_failure(DBusMessageIter *iter,
+                                               void *user_data)
+{
+       struct failure_data *data;
+       const char *str;
+
+       data = user_data;
+
+       connman_dbus_dict_append_basic(iter, "Type",
+                               DBUS_TYPE_STRING, &data->type_str);
+       str = "informational";
+       connman_dbus_dict_append_basic(iter, "Requirement",
+                               DBUS_TYPE_STRING, &str);
+
+       str = data->str;
+
+       /* Try to get information from provider about error */
+       if (!str)
+               str = vpn_provider_get_string(data->provider, data->key);
+
+       if (str)
+               connman_dbus_dict_append_basic(iter, "Value",
+                                               DBUS_TYPE_STRING, &str);
+}
+
+void vpn_agent_append_auth_failure(DBusMessageIter *iter,
+                               struct vpn_provider *provider,
+                               const char* information)
+{
+       struct failure_data data;
+       unsigned int value;
+
+       /* Skip if there are no auth errors */
+       value = vpn_provider_get_authentication_errors(provider);
+       if (!value)
+               return;
+
+       data.provider = provider;
+       data.type_str = "string";
+       data.key = "VpnAgent.AuthFailure";
+       data.str = information;
+
+       connman_dbus_dict_append_dict(iter, data.key,
+                               request_input_append_failure, &data);
+}
+
 int vpn_agent_check_and_process_reply_error(DBusMessage *reply,
                                struct vpn_provider *provider,
                                struct connman_task *task,
diff --git a/vpn/vpn-agent.h b/vpn/vpn-agent.h
index be7f9dd9..1dcaa4ec 100644
--- a/vpn/vpn-agent.h
+++ b/vpn/vpn-agent.h
@@ -38,6 +38,9 @@ bool vpn_agent_check_reply_has_dict(DBusMessage *reply);
 void vpn_agent_append_user_info(DBusMessageIter *iter,
                                struct vpn_provider *provider,
                                const char *username_str);
+void vpn_agent_append_auth_failure(DBusMessageIter *iter,
+                               struct vpn_provider *provider,
+                               const char *information);
 int vpn_agent_check_and_process_reply_error(DBusMessage *reply,
                                struct vpn_provider *provider,
                                struct connman_task *task,
-- 
2.20.1

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

Date: Fri, 11 Oct 2019 15:52:50 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH v5 0/9] Rewrite OpenConnect plugin and enhance support
        for VPN auth errors
To: [email protected]
Message-ID: <[email protected]>

This set of patches contains almost complete rewrite of OpenConnect VPN plugin,
introduces a method for informing VPN agent about authentication errors and
adds support for easier use of boolean type setting strings.

First of all, as the biggest change, OpenConnect VPN plugin is rewritten to
support the different authentication methods, which is configurable in provider
settings. If the configuration is omitted, cookie based authentication is set
as default. Support for automatic cookie (first use credentials to get cookie
and then connect with the cookie), credentials and separate public key with
private key and PKCS credential authentication is introduced. Credentials
and PKCS password are queried from VPN agent. Also support for the three
openconnect protocols is added also as provider settings for the OpenConnect
plugin. New options for OpenConnect are added as well to support allowing self
signed certificates and to toggle connection parameters, which may be required
with different server setups. Current approach utilizes screenscraping, which
should be replaced with libopenconnect use and guidelines for this are added
into TODO file.

Second, the authentication and connection errors are tracked by vpn-provider.c
when vpn_provider_indicate_error() is called with appropriate error code. These
errors can be utilized in VPN plugins to indicate VPN agent that saved
authentication credentials should be cleared. After succesful connection or
after saving provider settings the error counters are cleared. Main reason for
implementing these into provider is that saving the values in plugin private
data would be cleared after the connection is terminated, and provider is more
permanent during the runtime of vpnd.

And last, a new function to better support setting strings expected to be
boolean in value ("true" or "false") is implemented. This function can be used
to check if the setting string is explicitly the desired boolean value as the
default value in case of missing or invalid value is to be given.

This is a resent complete patch set with all versions bumped upwards, except
the last TODO one.

Changes since V2 and V3:
 * Correct PKCS lines, remove PKCS#12 references.
 * Update changed file contents as V1 cover letter was apparently sent.

Changes since V4:
 * Update list of commits and changes.

Changes since V5:
 * Update list of commits and changes.
 * Added comment and commit describing TODO for libopenconnect use.

Jussi Laakkonen (9):
  vpn-provider: Implement simple connection and auth error counters
  vpn-agent: Implement function to add auth failures to VPN agent msg
  doc: Add VpnAgent.AuthFailure to VPN agent API documentation
  vpn-provider: Implement setting string to bool conversion function
  openconnect: Rewrite plugin to support more auth methods and protocols
  openconnect: Use interactive mode when input to stdin is required
  doc: Add new OpenConnect PKCS parameters to VPN agent API
  doc: Add new OpenConnect configuration options to VPN config format
  TODO: Add task for libopenconnect use in OpenConnect VPN plugin

 TODO                      |   35 +
 doc/vpn-agent-api.txt     |   18 +
 doc/vpn-config-format.txt |   75 ++-
 vpn/plugins/openconnect.c | 1299 ++++++++++++++++++++++++++++++++-----
 vpn/vpn-agent.c           |   54 ++
 vpn/vpn-agent.h           |    3 +
 vpn/vpn-provider.c        |   57 +-
 vpn/vpn-provider.h        |    9 +
 8 files changed, 1389 insertions(+), 161 deletions(-)

-- 
2.20.1

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

Date: Fri, 11 Oct 2019 15:52:51 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH v3 1/9] vpn-provider: Implement simple connection and
        auth error counters
To: [email protected]
Message-ID: <[email protected]>

Add simple error counters to vpn-provider.c to count authentication and
connection errors separately. These can be used by the VPN plugins using
VPN agent to determine if the previous authentication has failed, and in
such case to re-request the credentials. Values are incremented when
vpn_provider_indicate_error() is called, both login errors
(VPN_PROVIDER_ERROR_LOGIN_FAILED) and authentication errors
(VPN_PROVIDER_ERROR_AUTH_FAILED) increase the authentication error
counter.

Counters are reset when connection succeeds (vpn-provider.c:connect_cb()
is called without error) or when the provider is saved (for the cases
where user updates the credential info in provider settings).

Removed changing of the provider state to idle in case of login error.
It is only necessary to record the errors and set state using VPN
driver.
---

Changes since V2:
 * Do not exceed charlimit in vpn-provider.c.

Changes since V3:
 * No need to use const in function returns, removed.
 * Add copyright.

 vpn/vpn-provider.c | 37 +++++++++++++++++++++++++++++++++----
 vpn/vpn-provider.h |  7 +++++++
 2 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/vpn/vpn-provider.c b/vpn/vpn-provider.c
index ff4bab9a..c368d26c 100644
--- a/vpn/vpn-provider.c
+++ b/vpn/vpn-provider.c
@@ -3,6 +3,7 @@
  *  ConnMan VPN daemon
  *
  *  Copyright (C) 2012-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
@@ -88,6 +89,8 @@ struct vpn_provider {
        struct connman_ipaddress *prev_ipv4_addr;
        struct connman_ipaddress *prev_ipv6_addr;
        void *plugin_data;
+       unsigned int auth_error_counter;
+       unsigned int conn_error_counter;
 };
 
 static void append_properties(DBusMessageIter *iter,
@@ -859,6 +862,14 @@ static gchar **create_network_list(GSList *networks, gsize 
*count)
        return result;
 }
 
+void reset_error_counters(struct vpn_provider *provider)
+{
+       if (!provider)
+               return;
+
+       provider->auth_error_counter = provider->conn_error_counter = 0;
+}
+
 static int vpn_provider_save(struct vpn_provider *provider)
 {
        GKeyFile *keyfile;
@@ -866,6 +877,8 @@ static int vpn_provider_save(struct vpn_provider *provider)
        DBG("provider %p immutable %s", provider,
                                        provider->immutable ? "yes" : "no");
 
+       reset_error_counters(provider);
+
        if (provider->immutable) {
                /*
                 * Do not save providers that are provisioned via .config
@@ -1134,8 +1147,10 @@ static void connect_cb(struct vpn_provider *provider, 
void *user_data,
                        vpn_provider_set_state(provider,
                                        VPN_PROVIDER_STATE_FAILURE);
                }
-       } else
+       } else {
+               reset_error_counters(provider);
                g_dbus_send_reply(connection, pending, DBUS_TYPE_INVALID);
+       }
 
        dbus_message_unref(pending);
 }
@@ -1656,12 +1671,14 @@ int vpn_provider_indicate_error(struct vpn_provider 
*provider,
 
        switch (error) {
        case VPN_PROVIDER_ERROR_UNKNOWN:
+               break;
        case VPN_PROVIDER_ERROR_CONNECT_FAILED:
+               ++provider->conn_error_counter;
                break;
 
-        case VPN_PROVIDER_ERROR_LOGIN_FAILED:
-        case VPN_PROVIDER_ERROR_AUTH_FAILED:
-               vpn_provider_set_state(provider, VPN_PROVIDER_STATE_IDLE);
+       case VPN_PROVIDER_ERROR_LOGIN_FAILED:
+       case VPN_PROVIDER_ERROR_AUTH_FAILED:
+               ++provider->auth_error_counter;
                break;
        }
 
@@ -2683,6 +2700,18 @@ const char *vpn_provider_get_path(struct vpn_provider 
*provider)
        return provider->path;
 }
 
+unsigned int vpn_provider_get_authentication_errors(
+                                               struct vpn_provider *provider)
+{
+       return provider->auth_error_counter;
+}
+
+unsigned int vpn_provider_get_connection_errors(
+                                               struct vpn_provider *provider)
+{
+       return provider->conn_error_counter;
+}
+
 void vpn_provider_change_address(struct vpn_provider *provider)
 {
        switch (provider->family) {
diff --git a/vpn/vpn-provider.h b/vpn/vpn-provider.h
index 9aaff583..0838b2e5 100644
--- a/vpn/vpn-provider.h
+++ b/vpn/vpn-provider.h
@@ -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
@@ -118,6 +119,12 @@ const char *vpn_provider_get_save_group(struct 
vpn_provider *provider);
 const char *vpn_provider_get_name(struct vpn_provider *provider);
 const char *vpn_provider_get_host(struct vpn_provider *provider);
 const char *vpn_provider_get_path(struct vpn_provider *provider);
+
+unsigned int vpn_provider_get_authentication_errors(
+                                       struct vpn_provider *provider);
+unsigned int vpn_provider_get_connection_errors(
+                                       struct vpn_provider *provider);
+
 void vpn_provider_change_address(struct vpn_provider *provider);
 void vpn_provider_clear_address(struct vpn_provider *provider, int family);
 
-- 
2.20.1

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

Date: Fri, 11 Oct 2019 15:52:59 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH 9/9] TODO: Add task for libopenconnect use in
        OpenConnect VPN plugin
To: [email protected]
Message-ID: <[email protected]>

Replace screenscraping with libopenconnect in the plugin.
---
 TODO | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)

diff --git a/TODO b/TODO
index c1694e3d..c10b8ce1 100644
--- a/TODO
+++ b/TODO
@@ -194,6 +194,41 @@ VPN
    ids and passphrases.
 
 
+- Change OpenConnect plugin to use libopenconnect
+
+   Priority: Medium
+   Complexity: C4
+
+   Current implementation of OpenConnect uses screenscraping and interactive
+   mode for accepting self signed certificates and reacting to PKCS pass
+   phrase requests. This should be replaced with libopenconnect use. It may be
+   worthwhile to attempt to replace the whole authentication with the use of
+   openconnect_obtain_cookie() whatever authentication type is used. This
+   would lead to using only the cookie when connecting (--cookie-on-stdin)
+   and would cleanup the code at run_connect().
+
+   The usage of stdout can be removed as unnecessary. Cookie should be
+   retrieved with openconnect_obtain_cookie(). Remove this also from
+   connman_task_run().
+
+   Function is_valid_protocol() must use openconnect_get_supported_protocols.
+   Also the static const char *protocols[] would be unnecessary.
+
+   Reading the stderr with byte-by-byte approach is to be removed, as well as
+   are the PKCS failures and requests in stderr IO channel processing.
+
+   The use of interactive mode toggle is to be removed. Non-interactive mode
+   must be used, which leads to using --syslog with each authentication type
+   as task arg.
+
+   If the peer certificate cannot be verified with normal means it is because
+   the peer certificate is self signed and the user setting
+   "AllowSelfSignedCert" has to be used for the verify certificate callback
+   reply. The callback for certificate validation must return zero if user has
+   allowed self signed certificates. In such case save the SHA1 fingerprint of
+   server certificate as it is done now, otherwise indicate error to
+   libopenconnect.
+
 Tools
 =====
 
-- 
2.20.1

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

Date: Fri, 11 Oct 2019 15:52:56 +0300
From: Jussi Laakkonen <[email protected]>
Subject: [PATCH v5 6/9] openconnect: Use interactive mode when input
        to stdin is required
To: [email protected]
Message-ID: <[email protected]>

Disable syslog (--syslog) and use interactive mode when OpenConnect
requires input from user. This is the case when PKCS file is encrypted
and pass phrase is requested. Changed also to prompt for the PKCS
password only when needed, instead of having it as mandatory for the
pkcs authentication method. Also, in case self signed certificates are
allowed the answer needs to be written to OpenConnect, thus, interactive
mode is required.

Since the request for PKCS file pass phrase is sent to stderr without
terminating newline character it is imperative to implement additional
way of reading the content. The request for PKCS pass phrase is read
byte by byte in interactive mode to support both encrypted and
unencrypted PKCS#8, and to keep support for PKCS#12. The pass phrase is
queried from VPN agent if needed and in case of error (decryption failed
is regarded as invalid pass phrase) connection is terminated.

Using the interactive mode also fixes the issue of previous commit that
self signed certificate acceptance ("yes") couldn't have been written to
stdin of OpenConnect process. This is because in non-interactive mode
(--non-inter) OpenConnect quits if the certificate is self signed and
server SHA1 fingerprint is not set with --servercert. With these changes
OpenConnect is started in interactive mode if self signed certificates
are allowed and there is no OpenConnect.ServerCert set. Otherwise,
non-interactive mode is used.

Set both the IO channels to have no encoding (rely on content as ASCII).
---

Changes since V3 (actually V2, but previous version should have been omitted):
 * Update commit message and subject to be consistent with content.
 * Removed PKCS#12 -> PKCS changes from this commit (merged with prev.)

Changes since v4:
 * Modify and rebase because of commit 0005 V5 changes.

Changes since V5:
 * Fix parameter check in request_input_credentials().
 * Use connman_info() instead of DBG().

 vpn/plugins/openconnect.c | 305 +++++++++++++++++++++++++++++++-------
 1 file changed, 255 insertions(+), 50 deletions(-)

diff --git a/vpn/plugins/openconnect.c b/vpn/plugins/openconnect.c
index 6657a1f3..77931dc2 100644
--- a/vpn/plugins/openconnect.c
+++ b/vpn/plugins/openconnect.c
@@ -48,6 +48,7 @@
 #include "vpn.h"
 
 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+#define OC_MAX_READBUF_LEN 128
 
 enum opt_type {
        OPT_STRING      = 0,
@@ -103,6 +104,7 @@ struct oc_private_data {
        GIOChannel *out_ch;
        GIOChannel *err_ch;
        enum oc_connect_type connect_type;
+       bool interactive;
 };
 
 static bool is_valid_protocol(const char* protocol)
@@ -489,6 +491,82 @@ static void clear_provider_credentials(struct vpn_provider 
*provider)
        }
 }
 
+typedef void (* request_input_reply_cb_t) (DBusMessage *reply,
+                                       void *user_data);
+
+static int request_input_credentials(struct oc_private_data *data,
+                       request_input_reply_cb_t cb);
+
+
+static void request_input_pkcs_reply(DBusMessage *reply, void *user_data)
+{
+       struct oc_private_data *data = user_data;
+       const char *password = NULL;
+       const char *key;
+       DBusMessageIter iter, dict;
+       int err;
+
+       connman_info("provider %p", data->provider);
+
+       if (!reply)
+               goto err;
+
+       err = vpn_agent_check_and_process_reply_error(reply, data->provider,
+                               data->task, data->cb, data->user_data);
+       if (err) {
+               /* Ensure cb is called only once */
+               data->cb = NULL;
+               data->user_data = NULL;
+               goto err;
+       }
+
+       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, "OpenConnect.PKCSPassword")) {
+                       dbus_message_iter_next(&entry);
+                       if (dbus_message_iter_get_arg_type(&entry)
+                                                       != DBUS_TYPE_VARIANT)
+                               break;
+                       dbus_message_iter_recurse(&entry, &value);
+                       if (dbus_message_iter_get_arg_type(&value)
+                                                       != DBUS_TYPE_STRING)
+                               break;
+                       dbus_message_iter_get_basic(&value, &password);
+                       vpn_provider_set_string_hide_value(data->provider, key,
+                                               password);
+               }
+
+               dbus_message_iter_next(&dict);
+       }
+
+       if (data->connect_type != OC_CONNECT_PKCS || !password)
+               goto err;
+
+       if (write_data(data->fd_in, password) != 0) {
+               connman_error("openconnect failed to take PKCS pass phrase on"
+                                       " stdin");
+               goto err;
+       }
+
+       clear_provider_credentials(data->provider);
+
+       return;
+err:
+       oc_connect_done(data, EACCES);
+}
+
 static gboolean io_channel_err_cb(GIOChannel *source, GIOCondition condition,
                        gpointer user_data)
 {
@@ -513,7 +591,18 @@ static gboolean io_channel_err_cb(GIOChannel *source, 
GIOCondition condition,
                                "Failed to open HTTPS connection to",
                                NULL
        };
-       const char *pkcs_failure = "Failed to decrypt PKCS";
+       /* Handle both PKCS#12 and PKCS#8 failures */
+       const char *pkcs_failures[] = {
+                               "Failed to decrypt PKCS#12 certificate file",
+                               "Failed to decrypt PKCS#8 certificate file",
+                               NULL
+       };
+       /* Handle both PKCS#12 and PKCS#8 requests */
+       const char *pkcs_requests[] = {
+                               "Enter PKCS#12 pass phrase",
+                               "Enter PKCS#8 pass phrase",
+                               NULL
+       };
        const char *server_key_hash = "    --servercert";
        char *str;
        bool close = false;
@@ -527,14 +616,60 @@ static gboolean io_channel_err_cb(GIOChannel *source, 
GIOCondition condition,
        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';
+       if ((condition & G_IO_IN)) {
+               gsize len;
+               int pos;
+
+               if (!data->interactive) {
+                       if (g_io_channel_read_line(source, &str, &len, NULL,
+                                               NULL) != G_IO_STATUS_NORMAL)
+                               err = EIO;
+                       else
+                               str[len - 1] = '\0';
+               } else {
+                       GIOStatus status;
+                       str = g_try_new0(char, OC_MAX_READBUF_LEN);
+                       if (!str)
+                               return G_SOURCE_REMOVE;
+
+                       for (pos = 0; pos < OC_MAX_READBUF_LEN - 1 ; ++pos) {
+                               status = g_io_channel_read_chars(source,
+                                               str+pos, 1, &len, NULL);
+
+                               if (status == G_IO_STATUS_EOF) {
+                                       break;
+                               } else if (status != G_IO_STATUS_NORMAL) {
+                                       err = EIO;
+                                       break;
+                               }
+
+                               /* Ignore control chars and digits at start */
+                               if (!pos && (g_ascii_iscntrl(str[pos]) ||
+                                               g_ascii_isdigit(str[pos])))
+                                       --pos;
+
+                               /* Read zero length or no more to read */
+                               if (!len || g_io_channel_get_buffer_condition(
+                                                       source) != G_IO_IN ||
+                                                       str[pos] == '\n')
+                                       break;
+                       }
+
+                       /*
+                        * When self signed certificates are allowed and server
+                        * SHA1 fingerprint is printed to stderr there is a
+                        * newline char at the end of SHA1 fingerprint.
+                        */
+                       if (str[pos] == '\n')
+                               str[pos] = '\0';
+               }
 
                connman_info("openconnect: %s", str);
 
-               if (g_str_has_prefix(str, server_key_hash)) {
+               if (err || !str || !*str) {
+                       connman_info("error reading from openconnect");
+               } else if (g_str_has_prefix(str, server_key_hash)) {
+                       const char *fingerprint;
                        int position;
                        bool allow_self_signed;
 
@@ -545,21 +680,23 @@ static gboolean io_channel_err_cb(GIOChannel *source, 
GIOCondition condition,
 
                        if (allow_self_signed) {
                                position = strlen(server_key_hash) + 1;
+                               fingerprint = g_strstrip(str + position);
 
                                connman_info("Set server key hash: \"%s\"",
-                                                       str + position);
+                                                       fingerprint);
 
                                vpn_provider_set_string(data->provider,
                                                "OpenConnect.ServerCert",
-                                               str + position);
+                                               fingerprint);
 
                                /*
                                 * 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");
+                                       connman_error("openconnect: cannot "
+                                               "write answer to certificate "
+                                               "accept request");
 
                        } else {
                                connman_warn("Self signed certificate is not "
@@ -573,17 +710,21 @@ static gboolean io_channel_err_cb(GIOChannel *source, 
GIOCondition condition,
                                close = true;
                                err = ECONNREFUSED;
                        }
-               } else if (g_str_has_prefix(str, pkcs_failure)) {
-                       connman_warn("invalid PKCS password: %s", str);
-                       /*
-                        * Close IO channel to avoid deadlock. This is because
-                        * after PKCS file decryption fails OpenConnect will
-                        * wait for a new pass phrase to be written to stdin,
-                        * blocking vpnd as well. Instead of waiting for a
-                        * timeout it is better to close the IO channel here.
-                        */
+               } else if (strv_contains_prefix(pkcs_failures, str)) {
+                       connman_warn("PKCS failure: %s", str);
                        close = true;
                        err = EACCES;
+               } else if (strv_contains_prefix(pkcs_requests, str)) {
+                       connman_info("PKCS file pass phrase request: %s", str);
+                       err = request_input_credentials(data,
+                                               request_input_pkcs_reply);
+
+                       if (err != -EINPROGRESS) {
+                               err = EACCES;
+                               close = true;
+                       } else {
+                               err = 0;
+                       }
                } else if (strv_contains_prefix(auth_failures, str)) {
                        connman_warn("authentication failed: %s", str);
                        err = EACCES;
@@ -633,8 +774,10 @@ static int run_connect(struct oc_private_data *data)
        const char *password = NULL;
        const char *certificate = NULL;
        const char *private_key;
+       const char *setting_str;
+       bool setting;
        bool use_stdout = false;
-       int fd_out;
+       int fd_out = -1;
        int fd_err;
        int err = 0;
 
@@ -717,14 +860,19 @@ static int run_connect(struct oc_private_data *data)
        case OC_CONNECT_PKCS:
                certificate = vpn_provider_get_string(provider,
                                        "OpenConnect.PKCSClientCert");
-               password = vpn_provider_get_string(data->provider,
-                                       "OpenConnect.PKCSPassword");
-               if (!certificate || !password) {
+               if (!certificate) {
                        err = -EACCES;
                        goto done;
                }
 
                connman_task_add_argument(task, "--certificate", certificate);
+
+               password = vpn_provider_get_string(data->provider,
+                                       "OpenConnect.PKCSPassword");
+               /* Add password only if it is has been set */
+               if (!password || !g_strcmp0(password, "-"))
+                       break;
+
                connman_task_add_argument(task, "--passwd-on-stdin", NULL);
                break;
        }
@@ -735,10 +883,41 @@ static int run_connect(struct oc_private_data *data)
 
        task_append_config_data(provider, task);
 
-       connman_task_add_argument(task, "--non-inter", NULL);
+       /*
+        * To clarify complex situation, if cookie is expected to be printed
+        * to stdout all other output must go to syslog. But with PKCS all
+        * output must be catched in order to get message about file decryption
+        * error. For this reason, the mode has to be interactive as well.
+        */
+       switch (data->connect_type) {
+       case OC_CONNECT_COOKIE:
+               /* fall through */
+       case OC_CONNECT_COOKIE_WITH_USERPASS:
+               /* fall through */
+       case OC_CONNECT_USERPASS:
+               /* fall through */
+       case OC_CONNECT_PUBLICKEY:
+               connman_task_add_argument(task, "--syslog", NULL);
+
+               setting = vpn_provider_get_boolean(provider,
+                                       "OpenConnect.AllowSelfSignedCert",
+                                       false);
+               setting_str = vpn_provider_get_string(provider,
+                                       "OpenConnect.ServerCert");
 
-       /* Output all to syslog to output cookie only to stdout */
-       connman_task_add_argument(task, "--syslog", NULL);
+               /*
+                * Run in interactive mode if self signed certificates are
+                * allowed and there is no set server SHA1 fingerprint.
+                */
+               if (setting_str || !setting)
+                       connman_task_add_argument(task, "--non-inter", NULL);
+               else
+                       data->interactive = true;
+               break;
+       case OC_CONNECT_PKCS:
+               data->interactive = true;
+               break;
+       }
 
        connman_task_add_argument(task, "--script", SCRIPTDIR "/vpn-script");
 
@@ -789,9 +968,12 @@ static int run_connect(struct oc_private_data *data)
        case OC_CONNECT_PUBLICKEY:
                break;
        case OC_CONNECT_PKCS:
+               if (!password || !g_strcmp0(password, "-"))
+                       break;
+
                if (write_data(data->fd_in, password) != 0) {
                        connman_error("openconnect failed to take PKCS "
-                                               "password on stdin");
+                                               "pass phrase on stdin");
                        err = -EIO;
                }
 
@@ -812,15 +994,32 @@ static int run_connect(struct oc_private_data *data)
 
        if (use_stdout) {
                data->out_ch = g_io_channel_unix_new(fd_out);
-               data->out_ch_id = g_io_add_watch(data->out_ch,
-                                       G_IO_IN | G_IO_ERR | G_IO_HUP,
-                                       (GIOFunc)io_channel_out_cb, data);
+
+               /* Use ASCII encoding only */
+               if (g_io_channel_set_encoding(data->out_ch, NULL, NULL) !=
+                                       G_IO_STATUS_NORMAL) {
+                       close_io_channel(data, data->out_ch);
+                       err = -EIO;
+               } else {
+                       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);
+
+       /* Use ASCII encoding only */
+       if (g_io_channel_set_encoding(data->err_ch, NULL, NULL) !=
+                               G_IO_STATUS_NORMAL) {
+               close_io_channel(data, data->err_ch);
+               err = -EIO;
+       } else {
+               data->err_ch_id = g_io_add_watch(data->err_ch,
+                                       G_IO_IN | G_IO_ERR | G_IO_HUP,
+                                       (GIOFunc)io_channel_err_cb, data);
+       }
 
 done:
        clear_provider_credentials(data->provider);
@@ -908,8 +1107,12 @@ static void request_input_append_to_dict(struct 
vpn_provider *provider,
 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;
-       const char *username = NULL, *password = NULL, *pkcspassword = NULL;
+       const char *cookie = NULL;
+       const char *servercert = NULL;
+       const char *vpnhost = NULL;
+       const char *username = NULL;
+       const char *password = NULL;
+       const char *pkcspassword = NULL;
        const char *key;
        DBusMessageIter iter, dict;
        int err;
@@ -1054,16 +1257,20 @@ out:
        free_private_data(data);
 }
 
-static int request_input_credentials(struct oc_private_data *data)
+static int request_input_credentials(struct oc_private_data *data,
+                       request_input_reply_cb_t cb)
 {
        DBusMessage *message;
-       const char *path, *agent_sender, *agent_path, *username;
+       const char *path;
+       const char *agent_sender;
+       const char *agent_path;
+       const char *username;
        DBusMessageIter iter;
        DBusMessageIter dict;
        int err;
        void *agent;
 
-       if (!data)
+       if (!data || !cb)
                return -ESRCH;
 
        connman_info("provider %p", data->provider);
@@ -1147,8 +1354,7 @@ static int request_input_credentials(struct 
oc_private_data *data)
        connman_dbus_dict_close(&iter, &dict);
 
        err = connman_agent_queue_message(data->provider, message,
-                       connman_timeout_input_request(),
-                       request_input_credentials_reply, data, agent);
+                       connman_timeout_input_request(), cb, data, agent);
 
        dbus_message_unref(message);
 
@@ -1189,7 +1395,11 @@ static int oc_connect(struct vpn_provider *provider,
                        const char *dbus_sender, void *user_data)
 {
        struct oc_private_data *data;
-       const char *vpnhost, *vpncookie, *certificate, *username, *password;
+       const char *vpnhost;
+       const char *vpncookie;
+       const char *certificate;
+       const char *username;
+       const char *password;
        const char *private_key;
        int err;
 
@@ -1267,14 +1477,9 @@ static int oc_connect(struct vpn_provider *provider,
                break;
        case OC_CONNECT_PKCS:
                certificate = vpn_provider_get_string(provider,
-                               "OpenConnect.PKCSClientCert");
-               if (certificate) {
-                       password = vpn_provider_get_string(provider,
-                               "OpenConnect.PKCSPassword");
-                       if (!password || !g_strcmp0(password, "-"))
-                               goto request_input;
-               } else {
-                       DBG("missing PKCS certificate");
+                                       "OpenConnect.PKCSClientCert");
+               if (!certificate) {
+                       connman_warn("missing PKCS certificate");
                        oc_connect_done(data, EACCES);
                        free_private_data(data);
                        return -EACCES;
@@ -1286,7 +1491,7 @@ static int oc_connect(struct vpn_provider *provider,
        return run_connect(data);
 
 request_input:
-       err = request_input_credentials(data);
+       err = request_input_credentials(data, request_input_credentials_reply);
        if (err != -EINPROGRESS) {
                oc_connect_done(data, err);
                vpn_provider_indicate_error(data->provider,
-- 
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 18
***************************************

Reply via email to