clients: enable VPN secrets for nmtui/nmcli secret agent (rh #975185)
authorJiří Klimeš <jklimes@redhat.com>
Thu, 19 Nov 2015 17:32:19 +0000 (18:32 +0100)
committerJiří Klimeš <jklimes@redhat.com>
Sat, 12 Dec 2015 16:37:30 +0000 (17:37 +0100)
It allows nmcli and nmtui to ask for VPN passwords and thus successfully
activate VPN connections.

https://bugzilla.redhat.com/show_bug.cgi?id=975185

clients/common/nm-secret-agent-simple.c
clients/common/nm-secret-agent-simple.h

index 6eaba27..0fd61e2 100644 (file)
@@ -13,7 +13,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  *
- * Copyright 2011-2013 Red Hat, Inc.
+ * Copyright 2011-2015 Red Hat, Inc.
  * Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
  */
 
@@ -33,6 +33,9 @@
 
 #include <string.h>
 
+#include <NetworkManager.h>
+#include <nm-vpn-service-plugin.h>
+
 #include "nm-default.h"
 #include "nm-secret-agent-simple.h"
 
@@ -157,6 +160,7 @@ nm_secret_agent_simple_secret_free (NMSecretAgentSimpleSecret *secret)
        g_free (secret->name);
        g_free (secret->prop_name);
        g_free (secret->value);
+       g_free (secret->vpn_property);
        g_free (real->property);
        g_clear_object (&real->setting);
 
@@ -167,20 +171,27 @@ static NMSecretAgentSimpleSecret *
 nm_secret_agent_simple_secret_new (const char *name,
                                    NMSetting  *setting,
                                    const char *property,
+                                   const char *vpn_property,
                                    gboolean    password)
 {
        NMSecretAgentSimpleSecretReal *real;
 
        real = g_slice_new0 (NMSecretAgentSimpleSecretReal);
        real->base.name = g_strdup (name);
-       real->base.prop_name = g_strdup_printf ("%s.%s", nm_setting_get_name (setting), property);
+       real->base.prop_name = vpn_property ?
+                                g_strdup_printf ("%s.%s.%s", nm_setting_get_name (setting), property, vpn_property) :
+                                g_strdup_printf ("%s.%s", nm_setting_get_name (setting), property);
+       real->base.vpn_property = g_strdup (vpn_property);
        real->base.password = password;
 
        if (setting) {
                real->setting = g_object_ref (setting);
                real->property = g_strdup (property);
 
-               g_object_get (setting, property, &real->base.value, NULL);
+               if (vpn_property)
+                       real->base.value = g_strdup (nm_setting_vpn_get_secret (NM_SETTING_VPN (setting), vpn_property));
+               else
+                       g_object_get (setting, property, &real->base.value, NULL);
        }
 
        return &real->base;
@@ -209,11 +220,13 @@ add_8021x_secrets (NMSecretAgentSimpleRequest *request,
                secret = nm_secret_agent_simple_secret_new (_("Username"),
                                                            NM_SETTING (s_8021x),
                                                            NM_SETTING_802_1X_IDENTITY,
+                                                           NULL,
                                                            FALSE);
                g_ptr_array_add (secrets, secret);
                secret = nm_secret_agent_simple_secret_new (_("Password"),
                                                            NM_SETTING (s_8021x),
                                                            NM_SETTING_802_1X_PASSWORD,
+                                                           NULL,
                                                            TRUE);
                g_ptr_array_add (secrets, secret);
                return TRUE;
@@ -223,11 +236,13 @@ add_8021x_secrets (NMSecretAgentSimpleRequest *request,
                secret = nm_secret_agent_simple_secret_new (_("Identity"),
                                                            NM_SETTING (s_8021x),
                                                            NM_SETTING_802_1X_IDENTITY,
+                                                           NULL,
                                                            FALSE);
                g_ptr_array_add (secrets, secret);
                secret = nm_secret_agent_simple_secret_new (_("Private key password"),
                                                            NM_SETTING (s_8021x),
                                                            NM_SETTING_802_1X_PRIVATE_KEY_PASSWORD,
+                                                           NULL,
                                                            TRUE);
                g_ptr_array_add (secrets, secret);
                return TRUE;
@@ -251,6 +266,7 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request,
                secret = nm_secret_agent_simple_secret_new (_("Password"),
                                                            NM_SETTING (s_wsec),
                                                            NM_SETTING_WIRELESS_SECURITY_PSK,
+                                                           NULL,
                                                            TRUE);
                g_ptr_array_add (secrets, secret);
                return TRUE;
@@ -265,6 +281,7 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request,
                secret = nm_secret_agent_simple_secret_new (_("Key"),
                                                            NM_SETTING (s_wsec),
                                                            key,
+                                                           NULL,
                                                            TRUE);
                g_free (key);
 
@@ -277,6 +294,7 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request,
                        secret = nm_secret_agent_simple_secret_new (_("Password"),
                                                                    NM_SETTING (s_wsec),
                                                                    NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
+                                                                   NULL,
                                                                    TRUE);
                        g_ptr_array_add (secrets, secret);
                        return TRUE;
@@ -300,21 +318,145 @@ add_pppoe_secrets (NMSecretAgentSimpleRequest *request,
        secret = nm_secret_agent_simple_secret_new (_("Username"),
                                                    NM_SETTING (s_pppoe),
                                                    NM_SETTING_PPPOE_USERNAME,
+                                                   NULL,
                                                    FALSE);
        g_ptr_array_add (secrets, secret);
        secret = nm_secret_agent_simple_secret_new (_("Service"),
                                                    NM_SETTING (s_pppoe),
                                                    NM_SETTING_PPPOE_SERVICE,
+                                                   NULL,
                                                    FALSE);
        g_ptr_array_add (secrets, secret);
        secret = nm_secret_agent_simple_secret_new (_("Password"),
                                                    NM_SETTING (s_pppoe),
                                                    NM_SETTING_PPPOE_PASSWORD,
+                                                   NULL,
                                                    TRUE);
        g_ptr_array_add (secrets, secret);
        return TRUE;
 }
 
+struct {
+       const char *name;
+       const char *ui_name;
+} typedef VpnPasswordName;
+
+static const VpnPasswordName *
+vpn_get_secret_names (const char *vpn_type)
+{
+       const char *type;
+       static VpnPasswordName generic_vpn_secrets[] = { {"password", N_("Password")}, {NULL, NULL} };
+       static VpnPasswordName vpnc_secrets[] = { {"Xauth password", N_("Password")},
+                                                 {"IPSec secret", N_("Group password")},
+                                                 {NULL, NULL} };
+       static VpnPasswordName swan_secrets[] = { {"xauthpassword", N_("Password")},
+                                                 {"pskvalue", N_("Group password")},
+                                                 {NULL, NULL} };
+       static VpnPasswordName openconnect_secrets[] = { {"gateway", N_("Gateway")},
+                                                        {"cookie", N_("Cookie")},
+                                                        {"gwcert", N_("Gateway certificate hash")},
+                                                        {NULL, NULL} };
+
+       if (!vpn_type)
+               return NULL;
+
+       if (g_str_has_prefix (vpn_type, NM_DBUS_INTERFACE))
+               type = vpn_type + strlen (NM_DBUS_INTERFACE) + 1;
+       else
+               type = vpn_type;
+
+       if (   !g_strcmp0 (type, "openvpn")
+           || !g_strcmp0 (type, "pptp")
+           || !g_strcmp0 (type, "iodine")
+           || !g_strcmp0 (type, "ssh")
+           || !g_strcmp0 (type, "l2tp")
+           || !g_strcmp0 (type, "fortisslvpn"))
+                return generic_vpn_secrets;
+       else if (!g_strcmp0 (type, "vpnc"))
+               return vpnc_secrets;
+       else if (   !g_strcmp0 (type, "openswan")
+                || !g_strcmp0 (type, "libreswan")
+                || !g_strcmp0 (type, "strongswan"))
+               return swan_secrets;
+       else if (!g_strcmp0 (type, "openconnect"))
+               return openconnect_secrets;
+       return NULL;
+}
+
+static NMSettingSecretFlags
+get_vpn_secret_flags (NMSettingVpn *s_vpn, const char *secret_name)
+{
+       NMSettingSecretFlags flags = NM_SETTING_SECRET_FLAG_NONE;
+       GHashTable *vpn_data;
+
+       g_object_get (s_vpn, NM_SETTING_VPN_DATA, &vpn_data, NULL);
+       nm_vpn_service_plugin_get_secret_flags (vpn_data, secret_name, &flags);
+       g_hash_table_unref (vpn_data);
+
+       return flags;
+}
+
+static void
+add_vpn_secret_helper (GPtrArray *secrets, NMSettingVpn *s_vpn, const char *name, const char *ui_name)
+{
+       NMSecretAgentSimpleSecret *secret;
+       NMSettingSecretFlags flags;
+       int i;
+
+       /* Check for duplicates */
+       for (i = 0; i < secrets->len; i++) {
+               secret = secrets->pdata[i];
+
+               if (g_strcmp0 (secret->vpn_property, name) == 0)
+                       return;
+       }
+
+       flags = get_vpn_secret_flags (s_vpn, name);
+       if (   flags & NM_SETTING_SECRET_FLAG_AGENT_OWNED
+           || flags & NM_SETTING_SECRET_FLAG_NOT_SAVED) {
+               secret = nm_secret_agent_simple_secret_new (ui_name,
+                                                           NM_SETTING (s_vpn),
+                                                           NM_SETTING_VPN_SECRETS,
+                                                           name,
+                                                           TRUE);
+               g_ptr_array_add (secrets, secret);
+       }
+}
+
+#define VPN_MSG_TAG "x-vpn-message:"
+
+static gboolean
+add_vpn_secrets (NMSecretAgentSimpleRequest *request,
+                 GPtrArray                  *secrets,
+                 char                       **msg)
+{
+       NMSettingVpn *s_vpn = nm_connection_get_setting_vpn (request->connection);
+       const VpnPasswordName *secret_names, *p;
+       char *tmp = NULL;
+       char **iter;
+
+       /* If hints are given, then always ask for what the hints require */
+       if (request->hints && g_strv_length (request->hints)) {
+               for (iter = request->hints; iter && *iter; iter++) {
+                       if (!tmp && g_str_has_prefix (*iter, VPN_MSG_TAG))
+                               tmp = g_strdup (*iter + strlen (VPN_MSG_TAG));
+                       else
+                               add_vpn_secret_helper (secrets, s_vpn, *iter, *iter);
+               }
+       }
+       if (msg)
+               *msg = g_strdup (tmp);
+
+       /* Now add what client thinks might be required, because hints may be empty or incomplete */
+       p = secret_names = vpn_get_secret_names (nm_setting_vpn_get_service_type (s_vpn));
+       while (p && p->name) {
+               add_vpn_secret_helper (secrets, s_vpn, p->name, _(p->ui_name));
+               p++;
+       }
+
+       return TRUE;
+}
+
 static void
 request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
 {
@@ -351,6 +493,7 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
                secret = nm_secret_agent_simple_secret_new (_("Network name"),
                                                            NM_SETTING (s_con),
                                                            NM_SETTING_CONNECTION_ID,
+                                                           NULL,
                                                            FALSE);
                g_ptr_array_add (secrets, secret);
                ok = add_8021x_secrets (request, secrets);
@@ -369,6 +512,7 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
                        secret = nm_secret_agent_simple_secret_new (_("PIN"),
                                                                    NM_SETTING (s_gsm),
                                                                    NM_SETTING_GSM_PIN,
+                                                                   NULL,
                                                                    FALSE);
                        g_ptr_array_add (secrets, secret);
                } else {
@@ -379,6 +523,7 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
                        secret = nm_secret_agent_simple_secret_new (_("Password"),
                                                                    NM_SETTING (s_gsm),
                                                                    NM_SETTING_GSM_PASSWORD,
+                                                                   NULL,
                                                                    TRUE);
                        g_ptr_array_add (secrets, secret);
                }
@@ -392,6 +537,7 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
                secret = nm_secret_agent_simple_secret_new (_("Password"),
                                                            NM_SETTING (s_cdma),
                                                            NM_SETTING_CDMA_PASSWORD,
+                                                           NULL,
                                                            TRUE);
                g_ptr_array_add (secrets, secret);
        } else if (nm_connection_is_type (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) {
@@ -408,8 +554,21 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
                secret = nm_secret_agent_simple_secret_new (_("Password"),
                                                            setting,
                                                            "password",
+                                                           NULL,
                                                            TRUE);
                g_ptr_array_add (secrets, secret);
+       } else if (nm_connection_is_type (request->connection, NM_SETTING_VPN_SETTING_NAME)) {
+               NMSettingConnection *s_con;
+
+               s_con = nm_connection_get_setting_connection (request->connection);
+
+               title = _("VPN password required");
+               msg = NULL;
+
+               ok = add_vpn_secrets (request, secrets, &msg);
+               if (!msg)
+                       msg = g_strdup_printf (_("A password is required to connect to '%s'."),
+                                              nm_connection_get_id (request->connection));
        } else
                ok = FALSE;
 
@@ -455,13 +614,6 @@ nm_secret_agent_simple_get_secrets (NMSecretAgentOld                 *agent,
        s_con = nm_connection_get_setting_connection (connection);
        connection_type = nm_setting_connection_get_connection_type (s_con);
 
-       if (!strcmp (connection_type, NM_SETTING_VPN_SETTING_NAME)) {
-               /* We don't support VPN secrets yet */
-               error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS,
-                                    "VPN secrets not supported");
-               goto nope;
-       }
-
        if (!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)) {
                /* We don't do stored passwords */
                error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_NO_SECRETS,
@@ -515,9 +667,13 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self,
 
        if (secrets) {
                GVariantBuilder conn_builder, *setting_builder;
+               GVariantBuilder vpn_secrets_builder;
                GHashTable *settings;
                GHashTableIter iter;
                const char *name;
+               const char *vpn_secrets_base_name = NULL;
+
+               g_variant_builder_init (&vpn_secrets_builder, G_VARIANT_TYPE ("a{ss}"));
 
                settings = g_hash_table_new (g_str_hash, g_str_equal);
                for (i = 0; i < secrets->len; i++) {
@@ -530,9 +686,23 @@ nm_secret_agent_simple_response (NMSecretAgentSimple *self,
                                                     setting_builder);
                        }
 
+                       if (secret->base.vpn_property) {
+                               /* VPN secrets need slightly different treatment.
+                                * "secrets" property is actually a hash table of secrets. */
+                               vpn_secrets_base_name = secret->property;
+                               g_variant_builder_add (&vpn_secrets_builder, "{ss}",
+                                                      secret->base.vpn_property, secret->base.value);
+                       } else {
+                               g_variant_builder_add (setting_builder, "{sv}",
+                                                      secret->property,
+                                                      g_variant_new_string (secret->base.value));
+                       }
+               }
+
+               if (vpn_secrets_base_name) {
                        g_variant_builder_add (setting_builder, "{sv}",
-                                              secret->property,
-                                              g_variant_new_string (secret->base.value));
+                                              vpn_secrets_base_name,
+                                              g_variant_builder_end (&vpn_secrets_builder));
                }
 
                g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION);
@@ -691,5 +861,6 @@ nm_secret_agent_simple_new (const char *name)
 {
        return g_initable_new (NM_TYPE_SECRET_AGENT_SIMPLE, NULL, NULL,
                               NM_SECRET_AGENT_OLD_IDENTIFIER, name,
+                              NM_SECRET_AGENT_OLD_CAPABILITIES, NM_SECRET_AGENT_CAPABILITY_VPN_HINTS,
                               NULL);
 }
index 81fec65..a812cfa 100644 (file)
@@ -13,7 +13,7 @@
  * You should have received a copy of the GNU General Public License
  * along with this program. If not, see <http://www.gnu.org/licenses/>.
  *
- * Copyright 2013 - 2014 Red Hat, Inc.
+ * Copyright 2013 - 2015 Red Hat, Inc.
  */
 
 #ifndef __NM_SECRET_AGENT_SIMPLE_H__
@@ -43,6 +43,7 @@ typedef struct {
 
 typedef struct {
        char *name, *prop_name, *value;
+       char *vpn_property;
        gboolean password;
 } NMSecretAgentSimpleSecret;