nm: Update auth-dialog
authorTobias Brunner <tobias@strongswan.org>
Mon, 5 Sep 2016 08:58:16 +0000 (10:58 +0200)
committerTobias Brunner <tobias@strongswan.org>
Mon, 5 Sep 2016 13:41:16 +0000 (15:41 +0200)
This updates the auth dialog so that passwords are properly retrieved
(e.g. for the nm-applet).  It also adds support for external UI mode and
properly handles secret flags.

src/frontends/gnome/auth-dialog/main.c
src/frontends/gnome/nm-strongswan-service.name.in

index ee794a6..b4432aa 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2015 Lubomir Rintel
  *
+ * Copyright (C) 2013-2016 Tobias Brunner
  * Copyright (C) 2008-2011 Martin Willi
  * HSR Hochschule fuer Technik Rapperswil
  *
 
 #define NM_DBUS_SERVICE_STRONGSWAN     "org.freedesktop.NetworkManager.strongswan"
 
-/**
- * Wait for quit input
- */
+#define KEYRING_UUID_TAG "connection-uuid"
+#define KEYRING_SN_TAG "setting-name"
+#define KEYRING_SK_TAG "setting-key"
+
+static const SecretSchema network_manager_secret_schema = {
+       "org.freedesktop.NetworkManager.Connection",
+       SECRET_SCHEMA_DONT_MATCH_NAME,
+       {
+               { KEYRING_UUID_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
+               { KEYRING_SN_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
+               { KEYRING_SK_TAG, SECRET_SCHEMA_ATTRIBUTE_STRING },
+               { NULL, 0 },
+       }
+};
+
+#define UI_KEYFILE_GROUP "VPN Plugin UI"
+
+static char *keyring_lookup_secret(const char *uuid, const char *secret_name)
+{
+       GHashTable *attrs;
+       GList *list;
+       char *secret = NULL;
+
+       attrs = secret_attributes_build(&network_manager_secret_schema,
+                                                                       KEYRING_UUID_TAG, uuid,
+                                                                       KEYRING_SN_TAG, NM_SETTING_VPN_SETTING_NAME,
+                                                                       KEYRING_SK_TAG, secret_name,
+                                                                       NULL);
+
+       list = secret_service_search_sync (NULL, &network_manager_secret_schema, attrs,
+                                                                          SECRET_SEARCH_ALL | SECRET_SEARCH_UNLOCK | SECRET_SEARCH_LOAD_SECRETS,
+                                                                          NULL, NULL);
+       if (list && list->data)
+       {
+               SecretItem *item = list->data;
+               SecretValue *value = secret_item_get_secret (item);
+
+               if (value)
+               {
+                       secret = g_strdup (secret_value_get (value, NULL));
+                       secret_value_unref (value);
+               }
+       }
+
+       g_list_free_full (list, g_object_unref);
+       g_hash_table_unref (attrs);
+       return secret;
+}
+
+static void keyfile_add_entry_info(GKeyFile *keyfile, const gchar *key, const gchar *value,
+                                                                  const gchar *label, gboolean is_secret, gboolean should_ask)
+{
+       g_key_file_set_string (keyfile, key, "Value", value);
+       g_key_file_set_string (keyfile, key, "Label", label);
+       g_key_file_set_boolean (keyfile, key, "IsSecret", is_secret);
+       g_key_file_set_boolean (keyfile, key, "ShouldAsk", should_ask);
+}
+
+static void keyfile_print_stdout (GKeyFile *keyfile)
+{
+       gchar *data;
+       gsize length;
+
+       data = g_key_file_to_data (keyfile, &length, NULL);
+
+       fputs (data, stdout);
+
+       g_free (data);
+}
+
+static gboolean get_secrets(const char *type, const char *uuid, const char *name, gboolean retry,
+                                                       gboolean allow_interaction, gboolean external_ui_mode,
+                                                       const char *in_pw, char **out_pw, NMSettingSecretFlags flags)
+{
+       NMAVpnPasswordDialog *dialog;
+       char *prompt, *pw = NULL;
+       const char *new_pw = NULL;
+       guint32 minlen = 0;
+
+       if (!(flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) &&
+               !(flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED))
+       {
+               if (in_pw)
+               {
+                       pw = g_strdup (in_pw);
+               }
+               else
+               {
+                       pw = keyring_lookup_secret (uuid, "password");
+               }
+       }
+       if (flags & NM_SETTING_SECRET_FLAG_NOT_REQUIRED)
+       {
+               g_free (pw);
+               return TRUE;
+       }
+       if (!strcmp(type, "eap"))
+       {
+               prompt = g_strdup_printf (_("EAP password required to establish VPN connection '%s'."),
+                                                                 name);
+       }
+       else if (!strcmp(type, "key"))
+       {
+               prompt = g_strdup_printf (_("Private key decryption password required to establish VPN connection '%s'."),
+                                                                 name);
+       }
+       else if (!strcmp(type, "psk"))
+       {
+               prompt = g_strdup_printf (_("Pre-shared key required to establish VPN connection '%s' (min. 20 characters)."),
+                                                                 name);
+               minlen = 20;
+       }
+       else /* smartcard */
+       {
+               prompt = g_strdup_printf (_("Smartcard PIN required to establish VPN connection '%s'."),
+                                                                 name);
+       }
+       if (external_ui_mode)
+       {
+               GKeyFile *keyfile;
+
+               keyfile = g_key_file_new ();
+
+               g_key_file_set_integer (keyfile, UI_KEYFILE_GROUP, "Version", 2);
+               g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Description", prompt);
+               g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Title", _("Authenticate VPN"));
+
+               keyfile_add_entry_info (keyfile, "password", pw ?: "", _("Password:"), TRUE, allow_interaction);
+
+               keyfile_print_stdout (keyfile);
+               g_key_file_unref (keyfile);
+               goto out;
+       }
+       else if (!allow_interaction ||
+                       (!retry && pw && !(flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)))
+       {
+               /* If we can't prompt the user, just return the existing password. Do the same
+                * if we don't nee a new password (!retry) and have an existing saved one */
+               *out_pw = pw;
+               g_free (prompt);
+               return TRUE;
+       }
+
+       dialog = (NMAVpnPasswordDialog*)nma_vpn_password_dialog_new(_("Authenticate VPN"), prompt, NULL);
+       nma_vpn_password_dialog_set_show_password_secondary(dialog, FALSE);
+
+       if (pw && !(flags & NM_SETTING_SECRET_FLAG_NOT_SAVED))
+       {
+               nma_vpn_password_dialog_set_password(dialog, pw);
+       }
+       gtk_widget_show (GTK_WIDGET (dialog));
+
+too_short_retry:
+       if (nma_vpn_password_dialog_run_and_block (dialog))
+       {
+               new_pw = nma_vpn_password_dialog_get_password(dialog);
+               if (new_pw && minlen && strlen(new_pw) < minlen)
+               {
+                       goto too_short_retry;
+               }
+               else if (new_pw)
+               {
+                       *out_pw = g_strdup (new_pw);
+               }
+       }
+       gtk_widget_hide (GTK_WIDGET (dialog));
+       gtk_widget_destroy (GTK_WIDGET (dialog));
+out:
+       g_free (prompt);
+       return TRUE;
+}
+
+static void print_secret (const char *secret_name, gchar *secret)
+{
+       if (secret)
+       {
+               printf("%s\n%s\n", secret_name, secret);
+               g_free(secret);
+       }
+       printf("\n\n");
+       fflush(stdout);
+}
+
 static void wait_for_quit (void)
 {
        GString *str;
@@ -62,45 +243,22 @@ static void wait_for_quit (void)
        g_string_free (str, TRUE);
 }
 
-/**
- * get the connection type
- */
-static char* get_connection_type(char *uuid)
-{
-       GHashTable *data = NULL, *secrets = NULL;
-       char *method;
-
-       if (!nm_vpn_service_plugin_read_vpn_details (0, &data, &secrets)) {
-               fprintf (stderr, "Failed to read data and secrets from stdin.\n");
-               return NULL;
-       }
-
-       method = g_hash_table_lookup (data, "method");
-       if (method)
-               method = g_strdup(method);
-
-       if (data)
-               g_hash_table_unref (data);
-       if (secrets)
-               g_hash_table_unref (secrets);
-
-       return method;
-}
-
 int main (int argc, char *argv[])
 {
-       gboolean retry = FALSE, allow_interaction = FALSE;
-       gchar *name = NULL, *uuid = NULL, *service = NULL, *pass;
+       gboolean retry = FALSE, allow_interaction = FALSE, external_ui_mode = FALSE;
+       gchar *name = NULL, *uuid = NULL, *service = NULL, *pass = NULL;
+       GHashTable *data = NULL, *secrets = NULL;
+       NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
        GOptionContext *context;
        char *agent, *type;
-       guint32 minlen = 0;
-       GtkWidget *dialog;
+       int status = 0;
        GOptionEntry entries[] = {
                { "reprompt", 'r', 0, G_OPTION_ARG_NONE, &retry, "Reprompt for passwords", NULL},
                { "uuid", 'u', 0, G_OPTION_ARG_STRING, &uuid, "UUID of VPN connection", NULL},
                { "name", 'n', 0, G_OPTION_ARG_STRING, &name, "Name of VPN connection", NULL},
                { "service", 's', 0, G_OPTION_ARG_STRING, &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},
                { NULL }
        };
 
@@ -128,96 +286,82 @@ int main (int argc, char *argv[])
                return 1;
        }
 
-       type = get_connection_type(uuid);
-       if (!type)
+       if (!nm_vpn_service_plugin_read_vpn_details (0, &data, &secrets))
        {
-               fprintf(stderr, "Connection lookup failed\n");
+               fprintf(stderr, "Failed to read '%s' (%s) data and secrets from stdin.\n",
+                               name, uuid);
                return 1;
        }
-       if (!strcmp(type, "eap") || !strcmp(type, "key") || !strcmp(type, "psk") ||
-               !strcmp(type, "smartcard"))
+
+       type = g_hash_table_lookup (data, "method");
+       if (!type)
        {
-               pass = secret_password_lookup_sync(SECRET_SCHEMA_COMPAT_NETWORK, NULL, NULL,
-                                                  "user", g_get_user_name(),
-                                                  "server", name,
-                                                  "protocol", service,
-                                                  NULL);
+               fprintf(stderr, "Connection lookup failed\n");
+               status = 1;
+               goto out;
+       }
 
-               if ((!pass || retry) && allow_interaction)
+       if (!strcmp(type, "eap") || !strcmp(type, "key") ||
+               !strcmp(type, "psk") || !strcmp(type, "smartcard"))
+       {
+               nm_vpn_service_plugin_get_secret_flags (secrets, "password", &flags);
+               if (!get_secrets(type, uuid, name, retry, allow_interaction, external_ui_mode,
+                                                g_hash_table_lookup (secrets, "password"), &pass, flags))
                {
-                       if (!strcmp(type, "eap"))
-                       {
-                               dialog = nma_vpn_password_dialog_new(_("VPN password required"),
-                                                                    _("EAP password required to establish VPN connection:"),
-                                                                    NULL);
-                       }
-                       else if (!strcmp(type, "key"))
-                       {
-                               dialog = nma_vpn_password_dialog_new(_("VPN password required"),
-                                                                    _("Private key decryption password required to establish VPN connection:"),
-                                                                    NULL);
-                       }
-                       else if (!strcmp(type, "psk"))
-                       {
-                               dialog = nma_vpn_password_dialog_new(_("VPN password required"),
-                                                                    _("Pre-shared key required to establish VPN connection (min. 20 characters):"),
-                                                                    NULL);
-                               minlen = 20;
-                       }
-                       else /* smartcard */
-                       {
-                               dialog = nma_vpn_password_dialog_new(_("VPN password required"),
-                                                                    _("Smartcard PIN required to establish VPN connection:"),
-                                                                    NULL);
-                       }
-                       if (pass)
-                       {
-                               nma_vpn_password_dialog_set_password(NMA_VPN_PASSWORD_DIALOG(dialog), pass);
-                       }
-
-                       nma_vpn_password_dialog_set_show_password_secondary(NMA_VPN_PASSWORD_DIALOG(dialog), FALSE);
-                       gtk_widget_show(dialog);
-too_short_retry:
-                       if (!nma_vpn_password_dialog_run_and_block(NMA_VPN_PASSWORD_DIALOG(dialog)))
-                       {
-                               return 1;
-                       }
-
-                       pass = g_strdup(nma_vpn_password_dialog_get_password(NMA_VPN_PASSWORD_DIALOG(dialog)));
-                       if (minlen && strlen(pass) < minlen)
-                       {
-                               goto too_short_retry;
-                       }
+                       status = 1;
                }
-               if (pass)
+               else if (!external_ui_mode)
                {
-                       printf("password\n%s\n", pass);
-                       g_free(pass);
+                       print_secret("password", pass);
+                       wait_for_quit ();
                }
        }
-       else
+       else if (!strcmp(type, "agent"))
        {
                agent = getenv("SSH_AUTH_SOCK");
                if (agent)
                {
-                       printf("agent\n%s\n", agent);
-               }
-               else
-               {
-                       if (allow_interaction)
+                       if (external_ui_mode)
                        {
-                               dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR,
-                                                         GTK_BUTTONS_OK,
-                                                         _("Configuration uses ssh-agent for authentication, "
-                                                         "but ssh-agent is not running!"));
-                               gtk_dialog_run (GTK_DIALOG (dialog));
-                               gtk_widget_destroy (dialog);
+                               GKeyFile *keyfile;
+
+                               keyfile = g_key_file_new ();
+
+                               g_key_file_set_integer (keyfile, UI_KEYFILE_GROUP, "Version", 2);
+                               g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Description", "SSH agent");
+                               g_key_file_set_string (keyfile, UI_KEYFILE_GROUP, "Title", _("Authenticate VPN"));
+
+                               keyfile_add_entry_info (keyfile, "agent", agent, "SSH agent socket", TRUE, FALSE);
+
+                               keyfile_print_stdout (keyfile);
+                               g_key_file_unref (keyfile);
+                       }
+                       else
+                       {
+                               print_secret("agent", g_strdup (agent));
+                               wait_for_quit ();
                        }
                }
+               else if (allow_interaction)
+               {
+                       GtkWidget *dialog;
+                       dialog = gtk_message_dialog_new(NULL, 0, GTK_MESSAGE_ERROR,
+                                                 GTK_BUTTONS_OK,
+                                                 _("Configuration uses ssh-agent for authentication, "
+                                                 "but ssh-agent is not running!"));
+                       gtk_dialog_run (GTK_DIALOG (dialog));
+                       gtk_widget_destroy (dialog);
+               }
        }
-       printf("\n\n");
-       /* flush output, wait for input */
-       fflush(stdout);
-       wait_for_quit ();
-       return 0;
+
+out:
+       if (data)
+       {
+               g_hash_table_unref (data);
+       }
+       if (secrets)
+       {
+               g_hash_table_unref(secrets);
+       }
+       return status;
 }
index d16e906..f52eec2 100644 (file)
@@ -9,3 +9,4 @@ plugin=@NM_PLUGINDIR@/libnm-vpn-plugin-strongswan.so
 [GNOME]
 auth-dialog=@NM_LIBEXECDIR@/nm-strongswan-auth-dialog
 properties=@NM_PLUGINDIR_ABS@/libnm-strongswan-properties
+supports-external-ui-mode=true