[NetworkManager-fortisslvpn/lr/connect-interactive] service: add support for interactive connect



commit 82048cab7e2a8be799774ef20c96c59d9bba375a
Author: Lubomir Rintel <lkundrak v3 sk>
Date:   Mon Apr 1 17:17:59 2019 +0200

    service: add support for interactive connect
    
    Make openfortivpn invoke a pinentry-compatible helper that proxies the
    password requests back to the daemon.
    
    Also, defer OTP from NeedSecrets() for interactive connection. Otherwise
    we're asking too early for poor souls who get OTP via SMS only once they
    initiated the authentication and supplied the password.

 .gitignore                          |   1 +
 Makefile.am                         |  18 ++++
 auth-dialog/main.c                  |  38 ++++++--
 nm-fortisslvpn-service.name.in      |   1 +
 src/nm-fortisslvpn-pinentry.c       | 139 +++++++++++++++++++++++++++
 src/nm-fortisslvpn-pppd-service.xml |  10 ++
 src/nm-fortisslvpn-service.c        | 185 ++++++++++++++++++++++++++++--------
 7 files changed, 343 insertions(+), 49 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index d3ba679..b2c8982 100644
--- a/.gitignore
+++ b/.gitignore
@@ -82,6 +82,7 @@ nm-fortisslvpn.desktop
 properties/resources.[ch]
 src/nm-fortisslvpn-pppd-service-dbus.c
 src/nm-fortisslvpn-pppd-service-dbus.h
+src/nm-fortisslvpn-pinentry
 src/nm-fortisslvpn-service
 
 /NetworkManager-fortisslvpn-*.tar*
diff --git a/Makefile.am b/Makefile.am
index b65fba9..c3e6fbd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -88,6 +88,7 @@ src_nm_fortisslvpn_service_SOURCES = \
 
 src_nm_fortisslvpn_service_CPPFLAGS = \
        $(src_cppflags) \
+        -DLIBEXECDIR=\"$(libexecdir)\" \
         -DPLUGINDIR=\"$(PPPD_PLUGIN_DIR)\"
 
 src_nm_fortisslvpn_service_LDADD = \
@@ -117,6 +118,23 @@ src_nm_fortisslvpn_pppd_plugin_la_LIBADD = \
 
 ###############################################################################
 
+libexec_PROGRAMS += src/nm-fortisslvpn-pinentry
+
+src_nm_fortisslvpn_pinentry_SOURCES = \
+       shared/nm-utils/nm-shared-utils.c \
+       shared/nm-utils/nm-shared-utils.h \
+       src/nm-fortisslvpn-pppd-service-dbus.h \
+       src/nm-fortisslvpn-pinentry.c
+
+src_nm_fortisslvpn_pinentry_CPPFLAGS = $(src_cppflags)
+
+src_nm_fortisslvpn_pinentry_LDADD = \
+       src/libnm-fortisslvpn-pppd-service-dbus.la \
+       $(GLIB_LIBS) \
+       $(LIBNM_LIBS)
+
+###############################################################################
+
 properties/resources.h: properties/gresource.xml
        $(AM_V_GEN) $(GLIB_COMPILE_RESOURCES) $< --target=$@ --sourcedir=$(srcdir)/properties 
--generate-header --internal
 
diff --git a/auth-dialog/main.c b/auth-dialog/main.c
index 45ddf35..6f60869 100644
--- a/auth-dialog/main.c
+++ b/auth-dialog/main.c
@@ -111,6 +111,7 @@ get_secrets (const char *vpn_uuid,
              gboolean retry,
              gboolean allow_interaction,
              gboolean external_ui_mode,
+             const char *hints[],
              const char *in_pw,
              char **out_password,
              char **out_otp,
@@ -120,6 +121,9 @@ get_secrets (const char *vpn_uuid,
        NMAVpnPasswordDialog *dialog;
        char *prompt, *pw = NULL;
        const char *new_password = NULL;
+       gboolean ask_password = FALSE;
+       gboolean ask_otp = FALSE;
+       int i;
 
        g_return_val_if_fail (vpn_uuid != NULL, FALSE);
        g_return_val_if_fail (vpn_name != NULL, FALSE);
@@ -137,9 +141,20 @@ get_secrets (const char *vpn_uuid,
                        pw = keyring_lookup_secret (vpn_uuid, NM_FORTISSLVPN_KEY_PASSWORD);
        }
 
+       if (hints) {
+               for (i = 0; hints[i]; i++) {
+                       if (strcmp (hints[i], "password") == 0)
+                               ask_password = TRUE;
+                       else if (strcmp (hints[i], "otp") == 0)
+                               ask_otp = TRUE;
+               }
+       } else {
+               ask_password = !(password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED);
+               ask_otp = (otp_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED);
+       }
+
        /* Don't ask if the no secrets are needed is unused */
-       if (   password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED
-           && !(otp_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)) {
+       if (!ask_password && !ask_otp) {
                g_free (pw);
                return TRUE;
        }
@@ -158,16 +173,16 @@ get_secrets (const char *vpn_uuid,
                g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Description", prompt);
                g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Title", _("Authenticate VPN"));
 
-               if (!(password_flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
+               if (ask_password)
                        keyfile_add_entry_info (keyfile, NM_FORTISSLVPN_KEY_PASSWORD, pw ? pw : "", 
_("Password:"), TRUE, allow_interaction);
-               if (otp_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
+               if (ask_otp)
                        keyfile_add_entry_info (keyfile, NM_FORTISSLVPN_KEY_OTP, "", _("Token:"), TRUE, 
allow_interaction);
 
                keyfile_print_stdout (keyfile);
                g_key_file_unref (keyfile);
                goto out;
        } else if (   allow_interaction == FALSE
-                  || (!retry && pw && !(password_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))) {
+                  || (!retry && pw && !(password_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) && !ask_otp)) {
                /* If interaction isn't allowed, just return existing secrets.
                 * Also, don't ask the user if we don't need a new password (ie, !retry),
                 * we have an existing PW, and the password is saved.
@@ -183,7 +198,7 @@ get_secrets (const char *vpn_uuid,
        dialog = (NMAVpnPasswordDialog *) nma_vpn_password_dialog_new (_("Authenticate VPN"), prompt, NULL);
 
        /* The one-time-password. */
-       if (otp_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) {
+       if (ask_otp) {
                nma_vpn_password_dialog_set_password_secondary_label (dialog, _("_Token:"));
                nma_vpn_password_dialog_set_show_password_secondary (dialog, TRUE);
        } else {
@@ -243,9 +258,10 @@ int
 main (int argc, char *argv[])
 {
        gboolean retry = FALSE, allow_interaction = FALSE, external_ui_mode = FALSE;
-       char *vpn_name = NULL;
-       char *vpn_uuid = NULL;
-       char *vpn_service = NULL;
+       gs_free char *vpn_name = NULL;
+       gs_free char *vpn_uuid = NULL;
+       gs_free char *vpn_service = NULL;
+       gs_strfreev char **hints = NULL;
        char *password = NULL;
        char *otp = NULL;
        GHashTable *data = NULL, *secrets = NULL;
@@ -259,6 +275,7 @@ main (int argc, char *argv[])
                        { "service", 's', 0, G_OPTION_ARG_STRING, &vpn_service, "VPN service type", NULL},
                        { "allow-interaction", 'i', 0, G_OPTION_ARG_NONE, &allow_interaction, "Allow user 
interaction", NULL},
                        { "external-ui-mode", 0, 0, G_OPTION_ARG_NONE, &external_ui_mode, "External UI mode", 
NULL},
+                       { "hint", 't', 0, G_OPTION_ARG_STRING_ARRAY, &hints, "Hints from the VPN plugin", 
NULL},
                        { NULL }
                };
 
@@ -291,7 +308,8 @@ main (int argc, char *argv[])
        nm_vpn_service_plugin_get_secret_flags (data, NM_FORTISSLVPN_KEY_PASSWORD, &password_flags);
        nm_vpn_service_plugin_get_secret_flags (data, NM_FORTISSLVPN_KEY_OTP, &otp_flags);
 
-       if (!get_secrets (vpn_uuid, vpn_name, retry, allow_interaction, external_ui_mode,
+       if (!get_secrets (vpn_uuid, vpn_name, retry, allow_interaction,
+                         external_ui_mode, (const char **) hints,
                          g_hash_table_lookup (secrets, NM_FORTISSLVPN_KEY_PASSWORD),
                          &password,
                          &otp,
diff --git a/nm-fortisslvpn-service.name.in b/nm-fortisslvpn-service.name.in
index 72b6ee8..02c485d 100644
--- a/nm-fortisslvpn-service.name.in
+++ b/nm-fortisslvpn-service.name.in
@@ -11,3 +11,4 @@ plugin=@PLUGINDIR@/libnm-vpn-plugin-fortisslvpn.so
 auth-dialog=@LIBEXECDIR@/nm-fortisslvpn-auth-dialog
 properties=@PLUGINDIR@/libnm-fortisslvpn-properties
 supports-external-ui-mode=true
+supports-hints=true
diff --git a/src/nm-fortisslvpn-pinentry.c b/src/nm-fortisslvpn-pinentry.c
new file mode 100644
index 0000000..91adaff
--- /dev/null
+++ b/src/nm-fortisslvpn-pinentry.c
@@ -0,0 +1,139 @@
+/* nm-fortisslvpn-pinentry - NetworkManager SSLVPN Password entry helper
+ *
+ * (C) 2019 Lubomir Rintel
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include "nm-default.h"
+
+#include <glib/gstdio.h>
+#include <gio/gunixinputstream.h>
+#include <gio/gunixoutputstream.h>
+
+#include "nm-fortisslvpn-pppd-service-dbus.h"
+#include "nm-fortisslvpn-service.h"
+#include "nm-utils/nm-shared-utils.h"
+#include "nm-utils/nm-vpn-plugin-macros.h"
+
+#define _NMLOG(level, ...) \
+    G_STMT_START { \
+         if (log_level >= (level)) { \
+             g_printerr ("nm-fortisslvpn[%s] %-7s [pinentry-%ld] " _NM_UTILS_MACRO_FIRST (__VA_ARGS__) "\n", 
\
+                         log_prefix_token, \
+                         nm_utils_syslog_to_str (level), \
+                         (long) getpid () \
+                         _NM_UTILS_MACRO_REST (__VA_ARGS__)); \
+         } \
+    } G_STMT_END
+
+#define _LOGI(...) _NMLOG(LOG_NOTICE,  __VA_ARGS__)
+#define _LOGW(...) _NMLOG(LOG_WARNING, __VA_ARGS__)
+#define _LOGE(...) _NMLOG(LOG_ERR, __VA_ARGS__)
+
+int
+main (int argc, char *argv[])
+{
+       gs_unref_object GInputStream *input_stream = g_unix_input_stream_new (STDIN_FILENO, FALSE);
+       gs_unref_object GDataInputStream *data_input = g_data_input_stream_new (input_stream);
+       gs_unref_object NMDBusFortisslvpnPpp *proxy = NULL;
+       const char *bus_name;
+       int log_level;
+       const char *log_prefix_token;
+       gs_free char *title = NULL;
+       gs_free char *desc = NULL;
+       gs_free char *prompt = NULL;
+       gs_free char *hint = NULL;
+       GError *error = NULL;
+       char *line;
+
+       bus_name = getenv ("NM_DBUS_SERVICE_FORTISSLVPN");
+       if (!bus_name)
+               bus_name = NM_DBUS_SERVICE_FORTISSLVPN;
+
+       log_level = _nm_utils_ascii_str_to_int64 (getenv ("NM_VPN_LOG_LEVEL"),
+                                                 10, 0, LOG_DEBUG,
+                                                 LOG_NOTICE);
+       log_prefix_token = getenv ("NM_VPN_LOG_PREFIX_TOKEN") ?: "???";
+
+       proxy = nmdbus_fortisslvpn_ppp_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                                              G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
+                                                              bus_name,
+                                                              NM_DBUS_PATH_FORTISSLVPN_PPP,
+                                                              NULL, &error);
+
+       if (!proxy) {
+               _LOGE ("couldn't create D-Bus proxy: %s", error->message);
+               g_clear_error (&error);
+               return 1;
+       }
+
+
+       g_print ("OK Pleased to meet you, but not as pleased as the acual pinentry program\n");
+
+       while (1) {
+               line = g_data_input_stream_read_line_utf8 (data_input, NULL, NULL, &error);
+               if (error) {
+                       _LOGE ("Error: %s\n", error->message);
+                       g_clear_error (&error);
+                       return 1;
+               }
+               if (!line)
+                       return 0;
+
+               if (g_str_has_prefix (line, "SETTITLE ")) {
+                       g_clear_pointer (&title, g_free);
+                       title = g_uri_unescape_string (strchr (line, ' ') + 1, NULL);
+                       g_print ("OK\n");
+               } else if (g_str_has_prefix (line, "SETDESC ")) {
+                       g_clear_pointer (&desc, g_free);
+                       desc = g_uri_unescape_string (strchr (line, ' ') + 1, NULL);
+                       g_print ("OK\n");
+               } else if (g_str_has_prefix (line, "SETPROMPT ")) {
+                       g_clear_pointer (&prompt, g_free);
+                       prompt = g_uri_unescape_string (strchr (line, ' ') + 1, NULL);
+                       g_print ("OK\n");
+               } else if (g_str_has_prefix (line, "SETKEYINFO ")) {
+                       g_clear_pointer (&hint, g_free);
+                       hint = g_uri_unescape_string (strchr (line, ' ') + 1, NULL);
+                       g_print ("OK\n");
+               } else if (strcmp (line, "GETPIN") == 0) {
+                       char *escaped;
+                       char *pin;
+
+                       if (nmdbus_fortisslvpn_ppp_call_get_pin_sync (proxy,
+                                                                     title ?: "",
+                                                                     desc ?: "",
+                                                                     prompt ?: "",
+                                                                     hint ?: "",
+                                                                     &pin, NULL, &error)) {
+                               escaped = g_uri_escape_string (pin, NULL, TRUE);
+                               g_free (pin);
+                               g_print ("D %s\nOK\n", escaped);
+                       } else {
+                               escaped = g_uri_escape_string (error->message, NULL, TRUE);
+                               g_print ("ERR %d %s\n", error->code, escaped);
+                               g_clear_error (&error);
+                       }
+                       g_free (escaped);
+               } else {
+                       /* You're not my real pinentry program! */
+                       g_printerr (line);
+                       g_print ("ERR 666 Not understood\n");
+               }
+               g_free (line);
+       }
+}
diff --git a/src/nm-fortisslvpn-pppd-service.xml b/src/nm-fortisslvpn-pppd-service.xml
index 97aca3a..b1caaa8 100644
--- a/src/nm-fortisslvpn-pppd-service.xml
+++ b/src/nm-fortisslvpn-pppd-service.xml
@@ -11,5 +11,15 @@
       <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_fortisslvpn_service_set_state"/>
       <arg name="state" type="u" direction="in"/>
     </method>
+
+    <method name="GetPin">
+      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="impl_fortisslvpn_service_get_pin"/>
+      <arg name="title" type="s" direction="in"/>
+      <arg name="desc" type="s" direction="in"/>
+      <arg name="prompt" type="s" direction="in"/>
+      <arg name="hint" type="s" direction="in"/> <!-- g_variant_get_child()'s real_new_secrets()
+                                                      relies on this being third. -->
+      <arg name="pin" type="s" direction="out"/>
+    </method>
   </interface>
 </node>
diff --git a/src/nm-fortisslvpn-service.c b/src/nm-fortisslvpn-service.c
index 8449bc4..c2105af 100644
--- a/src/nm-fortisslvpn-service.c
+++ b/src/nm-fortisslvpn-service.c
@@ -75,7 +75,8 @@ G_DEFINE_TYPE_WITH_CODE (NMFortisslvpnPlugin, nm_fortisslvpn_plugin, NM_TYPE_VPN
 
 typedef struct {
        GPid pid;
-       guint32 ppp_timeout_handler;
+       gboolean interactive;
+       GDBusMethodInvocation *get_pin_invocation;
        NMConnection *connection;
        char *config_file;
        NMDBusFortisslvpnPpp *dbus_skeleton;
@@ -123,6 +124,16 @@ cleanup_plugin (NMFortisslvpnPlugin *plugin)
 {
        NMFortisslvpnPluginPrivate *priv = NM_FORTISSLVPN_PLUGIN_GET_PRIVATE (plugin);
 
+       priv->interactive = FALSE;
+
+       if (priv->get_pin_invocation) {
+               g_dbus_method_invocation_return_error_literal (priv->get_pin_invocation,
+                                                              NMV_EDITOR_PLUGIN_ERROR,
+                                                              NMV_EDITOR_PLUGIN_ERROR_FAILED,
+                                                              "Disconnected");
+               priv->get_pin_invocation = NULL;
+       }
+
        if (priv->pid) {
                if (kill (priv->pid, SIGTERM) == 0)
                        g_timeout_add (2000, ensure_killed, GINT_TO_POINTER (priv->pid));
@@ -190,17 +201,6 @@ nm_find_openfortivpn (void)
        return *openfortivpn_binary;
 }
 
-static gboolean
-pppd_timed_out (gpointer user_data)
-{
-       NMFortisslvpnPlugin *plugin = NM_FORTISSLVPN_PLUGIN (user_data);
-
-       _LOGW ("Looks like pppd didn't initialize our dbus module");
-       nm_vpn_service_plugin_failure (NM_VPN_SERVICE_PLUGIN (plugin), NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED);
-
-       return FALSE;
-}
-
 static gboolean
 run_openfortivpn (NMFortisslvpnPlugin *plugin, NMSettingVpn *s_vpn, GError **error)
 {
@@ -260,6 +260,9 @@ run_openfortivpn (NMFortisslvpnPlugin *plugin, NMSettingVpn *s_vpn, GError **err
                g_ptr_array_add (argv, (gpointer) g_strdup (value));
        }
 
+       g_ptr_array_add (argv, (gpointer) g_strdup ("--pinentry"));
+       g_ptr_array_add (argv, (gpointer) g_strdup (LIBEXECDIR "/nm-fortisslvpn-pinentry"));
+
        g_ptr_array_add (argv, (gpointer) g_strdup ("--pppd-plugin"));
        g_ptr_array_add (argv, (gpointer) g_strdup (PLUGINDIR "/nm-fortisslvpn-pppd-plugin.so"));
 
@@ -285,33 +288,19 @@ run_openfortivpn (NMFortisslvpnPlugin *plugin, NMSettingVpn *s_vpn, GError **err
        priv->pid = pid;
        g_child_watch_add (pid, openfortivpn_watch_cb, plugin);
 
-       priv->ppp_timeout_handler = g_timeout_add (NM_FORTISSLVPN_WAIT_PPPD, pppd_timed_out, plugin);
-
        return TRUE;
 }
 
-static void
-remove_timeout_handler (NMFortisslvpnPlugin *plugin)
-{
-       NMFortisslvpnPluginPrivate *priv = NM_FORTISSLVPN_PLUGIN_GET_PRIVATE (plugin);
-
-       if (priv->ppp_timeout_handler) {
-               g_source_remove (priv->ppp_timeout_handler);
-               priv->ppp_timeout_handler = 0;
-       }
-}
-
 static gboolean
 handle_set_state (NMDBusFortisslvpnPpp *object,
                   GDBusMethodInvocation *invocation,
                   guint arg_state,
                   gpointer user_data)
 {
-       remove_timeout_handler (NM_FORTISSLVPN_PLUGIN (user_data));
        if (arg_state == NM_PPP_STATUS_DEAD || arg_state == NM_PPP_STATUS_DISCONNECT)
                nm_vpn_service_plugin_disconnect (NM_VPN_SERVICE_PLUGIN (user_data), NULL);
 
-       nmdbus_fortisslvpn_ppp_complete_set_state (object, invocation);
+       nmdbus_fortisslvpn_ppp_complete_set_state (object, invocation); /// remove me
        return TRUE;
 }
 
@@ -323,10 +312,69 @@ handle_set_ip4_config (NMDBusFortisslvpnPpp *object,
 {
        _LOGI ("FORTISSLVPN service (IP Config Get) reply received.");
 
-       remove_timeout_handler (NM_FORTISSLVPN_PLUGIN (user_data));
        nm_vpn_service_plugin_set_ip4_config (NM_VPN_SERVICE_PLUGIN (user_data), arg_config);
 
-       nmdbus_fortisslvpn_ppp_complete_set_ip4_config (object, invocation);
+       nmdbus_fortisslvpn_ppp_complete_set_ip4_config (object, invocation); /// remove me
+       return TRUE;
+}
+
+static gboolean
+handle_get_pin (NMDBusFortisslvpnPpp *object,
+                GDBusMethodInvocation *invocation,
+                const gchar *arg_title,
+                const gchar *arg_desc,
+                const gchar *arg_prompt,
+                const gchar *arg_hint,
+                gpointer user_data)
+{
+       NMFortisslvpnPlugin *plugin = NM_FORTISSLVPN_PLUGIN (user_data);
+       NMFortisslvpnPluginPrivate *priv = NM_FORTISSLVPN_PLUGIN_GET_PRIVATE (plugin);
+       const char *hints[] = { arg_hint, NULL };
+
+       _LOGI ("FORTISSLVPN service (%s) password request received.", arg_hint);
+
+       if (!priv->interactive) {
+               g_dbus_method_invocation_return_error_literal (invocation,
+                                                              NMV_EDITOR_PLUGIN_ERROR,
+                                                              NMV_EDITOR_PLUGIN_ERROR_FAILED,
+                                                              "More secrets required but cannot ask 
interactively");
+               return TRUE;
+       }
+
+       if (strcmp (arg_hint, NM_FORTISSLVPN_KEY_OTP) == 0) {
+               NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+               NMSetting *s_vpn = nm_connection_get_setting (priv->connection, NM_TYPE_SETTING_VPN);
+
+               g_return_val_if_fail (NM_IS_SETTING_VPN (s_vpn), FALSE);
+               nm_setting_get_secret_flags (s_vpn, arg_hint, &flags, NULL);
+               if ((flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) == 0) {
+                       g_dbus_method_invocation_return_error (invocation,
+                                                              NMV_EDITOR_PLUGIN_ERROR,
+                                                              NMV_EDITOR_PLUGIN_ERROR_FAILED,
+                                                              "Secret '%s' is not configured as required", 
arg_hint);
+                       return TRUE;
+               }
+       } else if (strcmp (arg_hint, NM_FORTISSLVPN_KEY_PASSWORD) != 0) {
+               /* nm_fortisslvpn_properties_validate_secrets()() is not tolerant
+                * towards unknown secrets. Don't make NetworkManager add one. */
+               g_dbus_method_invocation_return_error (invocation,
+                                                      NMV_EDITOR_PLUGIN_ERROR,
+                                                      NMV_EDITOR_PLUGIN_ERROR_FAILED,
+                                                      "Secret '%s' is not supported", arg_hint);
+       }
+
+       if (priv->get_pin_invocation) {
+               /* Should not happen! */
+               g_dbus_method_invocation_return_error_literal (priv->get_pin_invocation,
+                                                              NMV_EDITOR_PLUGIN_ERROR,
+                                                              NMV_EDITOR_PLUGIN_ERROR_FAILED,
+                                                              "Superseded by anoher GetPin() call");
+       }
+
+       priv->get_pin_invocation = invocation;
+       nm_vpn_service_plugin_secrets_required (NM_VPN_SERVICE_PLUGIN (plugin),
+                                               arg_prompt, hints);
+
        return TRUE;
 }
 
@@ -422,6 +470,15 @@ real_connect (NMVpnServicePlugin *plugin, NMConnection *connection, GError **err
        return run_openfortivpn (NM_FORTISSLVPN_PLUGIN (plugin), s_vpn, error);
 }
 
+static gboolean
+real_connect_interactive (NMVpnServicePlugin *plugin, NMConnection *connection,
+                          GVariant *details, GError **error)
+{
+       NMFortisslvpnPluginPrivate *priv = NM_FORTISSLVPN_PLUGIN_GET_PRIVATE (plugin);
+       priv->interactive = TRUE;
+       return real_connect (plugin, connection, error);
+}
+
 static gboolean
 real_need_secrets (NMVpnServicePlugin *plugin,
                    NMConnection *connection,
@@ -444,12 +501,6 @@ real_need_secrets (NMVpnServicePlugin *plugin,
            && !nm_setting_vpn_get_secret (NM_SETTING_VPN (s_vpn), NM_FORTISSLVPN_KEY_PASSWORD))
                return TRUE;
 
-       /* Do we require the one-time-password and don't have it? */
-       nm_setting_get_secret_flags (NM_SETTING (s_vpn), NM_FORTISSLVPN_KEY_OTP, &flags, NULL);
-       if (   (flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
-           && !nm_setting_vpn_get_secret (NM_SETTING_VPN (s_vpn), NM_FORTISSLVPN_KEY_OTP))
-               return TRUE;
-
        /* Otherwise we're fine */
        *setting_name = NULL;
        return FALSE;
@@ -462,21 +513,73 @@ real_disconnect (NMVpnServicePlugin *plugin, GError **err)
        return TRUE;
 }
 
+static gboolean
+real_new_secrets (NMVpnServicePlugin *plugin, NMConnection *connection, GError **error)
+{
+       NMFortisslvpnPluginPrivate *priv = NM_FORTISSLVPN_PLUGIN_GET_PRIVATE (plugin);
+       GVariant *parameters;
+       NMSettingVpn *s_vpn;
+       const char *hint = NULL;
+       const char *pin;
+
+       if (!priv->get_pin_invocation) {
+               g_set_error (error,
+                            NM_VPN_PLUGIN_ERROR,
+                            NM_VPN_PLUGIN_ERROR_LAUNCH_FAILED,
+                            "%s",
+                            _("Got new secrets, but nobody asked for them."));
+               return FALSE;
+       }
+
+       s_vpn = NM_SETTING_VPN (nm_connection_get_setting (connection, NM_TYPE_SETTING_VPN));
+       if (!s_vpn) {
+               g_dbus_method_invocation_return_error_literal (priv->get_pin_invocation,
+                                                              NMV_EDITOR_PLUGIN_ERROR,
+                                                              NMV_EDITOR_PLUGIN_ERROR_FAILED,
+                                                              "Connection lacked VPN setting");
+               goto out;
+       }
+
+       parameters = g_dbus_method_invocation_get_parameters (priv->get_pin_invocation);
+       g_variant_get_child (parameters, 3 /* hint */, "&s", &hint);
+       if (!hint) {
+               /* Can not happen. */
+               g_dbus_method_invocation_return_error_literal (priv->get_pin_invocation,
+                                                              NMV_EDITOR_PLUGIN_ERROR,
+                                                              NMV_EDITOR_PLUGIN_ERROR_FAILED,
+                                                              "Forgotten the secrets hint?");
+               goto out;
+       }
+
+       pin = nm_setting_vpn_get_secret (s_vpn, hint);
+       if (!pin) {
+               g_dbus_method_invocation_return_error (priv->get_pin_invocation,
+                                                      NMV_EDITOR_PLUGIN_ERROR,
+                                                      NMV_EDITOR_PLUGIN_ERROR_FAILED,
+                                                      "No '%s' hint in VPN setting", hint);
+               goto out;
+       }
+
+       nmdbus_fortisslvpn_ppp_complete_get_pin (priv->dbus_skeleton,
+                                                priv->get_pin_invocation,
+                                                pin);
+
+out:
+       priv->get_pin_invocation = NULL;
+       return TRUE;
+}
+
 static void
 state_changed_cb (GObject *object, NMVpnServiceState state, gpointer user_data)
 {
        NMFortisslvpnPluginPrivate *priv = NM_FORTISSLVPN_PLUGIN_GET_PRIVATE (object);
 
        switch (state) {
-       case NM_VPN_SERVICE_STATE_STARTED:
-               remove_timeout_handler (NM_FORTISSLVPN_PLUGIN (object));
-               break;
        case NM_VPN_SERVICE_STATE_UNKNOWN:
        case NM_VPN_SERVICE_STATE_INIT:
        case NM_VPN_SERVICE_STATE_SHUTDOWN:
        case NM_VPN_SERVICE_STATE_STOPPING:
        case NM_VPN_SERVICE_STATE_STOPPED:
-               remove_timeout_handler (NM_FORTISSLVPN_PLUGIN (object));
                g_clear_object (&priv->connection);
                break;
        default:
@@ -500,6 +603,7 @@ dispose (GObject *object)
                        g_dbus_interface_skeleton_unexport (skeleton);
                g_signal_handlers_disconnect_by_func (skeleton, handle_set_state, object);
                g_signal_handlers_disconnect_by_func (skeleton, handle_set_ip4_config, object);
+               g_signal_handlers_disconnect_by_func (skeleton, handle_get_pin, object);
        }
 
        G_OBJECT_CLASS (nm_fortisslvpn_plugin_parent_class)->dispose (object);
@@ -521,8 +625,10 @@ nm_fortisslvpn_plugin_class_init (NMFortisslvpnPluginClass *fortisslvpn_class)
        /* virtual methods */
        object_class->dispose = dispose;
        parent_class->connect = real_connect;
+       parent_class->connect_interactive = real_connect_interactive;
        parent_class->need_secrets = real_need_secrets;
        parent_class->disconnect = real_disconnect;
+       parent_class->new_secrets = real_new_secrets;
 }
 
 static GInitableIface *ginitable_parent_iface = NULL;
@@ -555,6 +661,7 @@ init_sync (GInitable *object, GCancellable *cancellable, GError **error)
 
        g_signal_connect (priv->dbus_skeleton, "handle-set-state", G_CALLBACK (handle_set_state), object);
        g_signal_connect (priv->dbus_skeleton, "handle-set-ip4-config", G_CALLBACK (handle_set_ip4_config), 
object);
+       g_signal_connect (priv->dbus_skeleton, "handle-get-pin", G_CALLBACK (handle_get_pin), object);
 
        g_object_unref (connection);
        return TRUE;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]