From 9d18afb0e59f2c7289aa6413120783b905a8e094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Thu, 19 Nov 2015 18:32:19 +0100 Subject: [PATCH 1/8] clients: enable VPN secrets for nmtui/nmcli secret agent (rh #975185) 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 (cherry picked from commit de86c23fbebd461d4264d39e3cc46594ad62c096) --- clients/common/nm-secret-agent-simple.c | 211 ++++++++++++++++++++++-- clients/common/nm-secret-agent-simple.h | 3 +- 2 files changed, 201 insertions(+), 13 deletions(-) diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c index 8eddb4400e..8e43c376cb 100644 --- a/clients/common/nm-secret-agent-simple.c +++ b/clients/common/nm-secret-agent-simple.c @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - * Copyright 2011-2013 Red Hat, Inc. + * Copyright 2011-2015 Red Hat, Inc. * Copyright 2011 Giovanni Campagna */ @@ -32,8 +32,13 @@ #include "config.h" #include +#include +#include #include +#include +#include + #include "nm-secret-agent-simple.h" G_DEFINE_TYPE (NMSecretAgentSimple, nm_secret_agent_simple, NM_TYPE_SECRET_AGENT_OLD) @@ -157,6 +162,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 +173,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 +222,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 +238,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 +268,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 +283,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 +296,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 +320,159 @@ 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; + char *flag_name; + const char *val; + unsigned long tmp; + + g_object_get (s_vpn, NM_SETTING_VPN_DATA, &vpn_data, NULL); + + flag_name = g_strdup_printf ("%s-flags", secret_name); + + /* Try new flags value first */ + val = g_hash_table_lookup (vpn_data, flag_name); + if (val) { + errno = 0; + tmp = strtoul (val, NULL, 10); + if (errno == 0 && tmp <= NM_SETTING_SECRET_FLAGS_ALL) + flags = (NMSettingSecretFlags) tmp; + } + g_free (flag_name); + 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 +509,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 +528,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 +539,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 +553,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 +570,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 +630,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 +683,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 +702,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 +877,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); } diff --git a/clients/common/nm-secret-agent-simple.h b/clients/common/nm-secret-agent-simple.h index 81fec65139..a812cfacef 100644 --- a/clients/common/nm-secret-agent-simple.h +++ b/clients/common/nm-secret-agent-simple.h @@ -13,7 +13,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . * - * 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; From dca2ded7e8bd38eaed7e1e79f92cbfa10917d493 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Fri, 27 Nov 2015 11:09:20 +0100 Subject: [PATCH 2/8] clients: move vpn_get_secret_names() to nm-vpn-helpers It should eventually move into libnm and ideally the data be obtained from VPN plugins. (No functional change, only moving the function). (cherry picked from commit 6dd1e2673ece6e962f5bfc3065929963fa954c0c) --- clients/cli/Makefile.am | 6 ++- clients/common/nm-secret-agent-simple.c | 50 +------------------------ clients/common/nm-vpn-helpers.c | 45 +++++++++++++++++++++- clients/common/nm-vpn-helpers.h | 9 ++++- clients/tui/Makefile.am | 4 ++ po/POTFILES.in | 1 + 6 files changed, 64 insertions(+), 51 deletions(-) diff --git a/clients/cli/Makefile.am b/clients/cli/Makefile.am index eef99cba71..2f394098a9 100644 --- a/clients/cli/Makefile.am +++ b/clients/cli/Makefile.am @@ -14,7 +14,9 @@ AM_CPPFLAGS = \ $(GLIB_CFLAGS) \ -DG_LOG_DOMAIN=\""nmcli"\" \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ - -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" + -DNMCLI_LOCALEDIR=\"$(datadir)/locale\" \ + -DNMCONFDIR=\"$(nmconfdir)\" \ + -DNMLIBDIR=\"$(libdir)\" nmcli_SOURCES = \ agent.c \ @@ -38,6 +40,8 @@ nmcli_SOURCES = \ \ $(srcdir)/../common/nm-secret-agent-simple.c \ $(srcdir)/../common/nm-secret-agent-simple.h \ + $(srcdir)/../common/nm-vpn-helpers.c \ + $(srcdir)/../common/nm-vpn-helpers.h \ $(NULL) nmcli_LDADD = \ diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c index 8e43c376cb..d8cfdce008 100644 --- a/clients/common/nm-secret-agent-simple.c +++ b/clients/common/nm-secret-agent-simple.c @@ -39,6 +39,7 @@ #include #include +#include "nm-vpn-helpers.h" #include "nm-secret-agent-simple.h" G_DEFINE_TYPE (NMSecretAgentSimple, nm_secret_agent_simple, NM_TYPE_SECRET_AGENT_OLD) @@ -338,53 +339,6 @@ add_pppoe_secrets (NMSecretAgentSimpleRequest *request, 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) { @@ -464,7 +418,7 @@ add_vpn_secrets (NMSecretAgentSimpleRequest *request, *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)); + p = secret_names = nm_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++; diff --git a/clients/common/nm-vpn-helpers.c b/clients/common/nm-vpn-helpers.c index eaa0917158..121b87a129 100644 --- a/clients/common/nm-vpn-helpers.c +++ b/clients/common/nm-vpn-helpers.c @@ -28,9 +28,9 @@ #include #include #include +#include #include -#include "nm-vpn-editor-plugin.h" #include "nm-vpn-helpers.h" @@ -225,3 +225,46 @@ nm_vpn_supports_ipv6 (NMConnection *connection) capabilities = nm_vpn_editor_plugin_get_capabilities (plugin); return (capabilities & NM_VPN_EDITOR_PLUGIN_CAPABILITY_IPV6) != 0; } + +const VpnPasswordName * +nm_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; +} + diff --git a/clients/common/nm-vpn-helpers.h b/clients/common/nm-vpn-helpers.h index 7ec21a71e2..50e1251004 100644 --- a/clients/common/nm-vpn-helpers.h +++ b/clients/common/nm-vpn-helpers.h @@ -20,14 +20,21 @@ #define __NM_VPN_HELPERS_H__ #include -#include +#include #include +struct { + const char *name; + const char *ui_name; +} typedef VpnPasswordName; + GSList *nm_vpn_get_plugins (GError **error); NMVpnEditorPlugin *nm_vpn_get_plugin_by_service (const char *service); gboolean nm_vpn_supports_ipv6 (NMConnection *connection); +const VpnPasswordName * nm_vpn_get_secret_names (const char *vpn_type); + #endif /* __NM_VPN_HELPERS_H__ */ diff --git a/clients/tui/Makefile.am b/clients/tui/Makefile.am index f6d93be2b4..a84fcdffe3 100644 --- a/clients/tui/Makefile.am +++ b/clients/tui/Makefile.am @@ -18,6 +18,8 @@ AM_CPPFLAGS= \ -DG_LOG_DOMAIN=\""nmtui"\" \ -DNM_VERSION_MAX_ALLOWED=NM_VERSION_NEXT_STABLE \ -DLOCALEDIR=\""$(localedir)"\" \ + -DNMCONFDIR=\"$(nmconfdir)\" \ + -DNMLIBDIR=\"$(libdir)\" \ $(NULL) bin_PROGRAMS = nmtui @@ -116,6 +118,8 @@ nmtui_SOURCES = \ nmt-widget-list.h \ $(srcdir)/../common/nm-secret-agent-simple.c \ $(srcdir)/../common/nm-secret-agent-simple.h \ + $(srcdir)/../common/nm-vpn-helpers.c \ + $(srcdir)/../common/nm-vpn-helpers.h \ $(NULL) nmtui_LDADD = \ diff --git a/po/POTFILES.in b/po/POTFILES.in index 6c09b21ff0..19ef9df384 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -12,6 +12,7 @@ clients/cli/settings.c clients/cli/utils.c clients/common/nm-polkit-listener.c clients/common/nm-secret-agent-simple.c +clients/common/nm-vpn-helpers.c clients/nm-online.c clients/tui/newt/nmt-newt-utils.c clients/tui/nm-editor-utils.c From d6b98449a814cb54c5a62dbca9342445e340e754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Fri, 11 Dec 2015 09:54:40 +0100 Subject: [PATCH 3/8] clients: add vpn_type member to secret structure in secret agent It is useful to know what VPN type the secret belongs to. (cherry picked from commit 8dfafac17c1870119330d5e42a4df3a6e8ac3bc8) --- clients/common/nm-secret-agent-simple.c | 19 +++++++++++++++++++ clients/common/nm-secret-agent-simple.h | 1 + 2 files changed, 20 insertions(+) diff --git a/clients/common/nm-secret-agent-simple.c b/clients/common/nm-secret-agent-simple.c index d8cfdce008..6cb6c58db7 100644 --- a/clients/common/nm-secret-agent-simple.c +++ b/clients/common/nm-secret-agent-simple.c @@ -164,6 +164,7 @@ nm_secret_agent_simple_secret_free (NMSecretAgentSimpleSecret *secret) g_free (secret->prop_name); g_free (secret->value); g_free (secret->vpn_property); + g_free (secret->vpn_type); g_free (real->property); g_clear_object (&real->setting); @@ -175,6 +176,7 @@ nm_secret_agent_simple_secret_new (const char *name, NMSetting *setting, const char *property, const char *vpn_property, + const char *vpn_type, gboolean password) { NMSecretAgentSimpleSecretReal *real; @@ -185,6 +187,7 @@ nm_secret_agent_simple_secret_new (const char *name, 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.vpn_type = g_strdup (vpn_type); real->base.password = password; if (setting) { @@ -224,12 +227,14 @@ add_8021x_secrets (NMSecretAgentSimpleRequest *request, NM_SETTING (s_8021x), NM_SETTING_802_1X_IDENTITY, NULL, + 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, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; @@ -240,12 +245,14 @@ add_8021x_secrets (NMSecretAgentSimpleRequest *request, NM_SETTING (s_8021x), NM_SETTING_802_1X_IDENTITY, NULL, + 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, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; @@ -270,6 +277,7 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request, NM_SETTING (s_wsec), NM_SETTING_WIRELESS_SECURITY_PSK, NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; @@ -285,6 +293,7 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request, NM_SETTING (s_wsec), key, NULL, + NULL, TRUE); g_free (key); @@ -298,6 +307,7 @@ add_wireless_secrets (NMSecretAgentSimpleRequest *request, NM_SETTING (s_wsec), NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD, NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; @@ -322,18 +332,21 @@ add_pppoe_secrets (NMSecretAgentSimpleRequest *request, NM_SETTING (s_pppoe), NM_SETTING_PPPOE_USERNAME, NULL, + 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, + 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, + NULL, TRUE); g_ptr_array_add (secrets, secret); return TRUE; @@ -388,6 +401,7 @@ add_vpn_secret_helper (GPtrArray *secrets, NMSettingVpn *s_vpn, const char *name NM_SETTING (s_vpn), NM_SETTING_VPN_SECRETS, name, + nm_setting_vpn_get_service_type (s_vpn), TRUE); g_ptr_array_add (secrets, secret); } @@ -464,6 +478,7 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) NM_SETTING (s_con), NM_SETTING_CONNECTION_ID, NULL, + NULL, FALSE); g_ptr_array_add (secrets, secret); ok = add_8021x_secrets (request, secrets); @@ -482,6 +497,7 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) secret = nm_secret_agent_simple_secret_new (_("PIN"), NM_SETTING (s_gsm), NM_SETTING_GSM_PIN, + NULL, NULL, FALSE); g_ptr_array_add (secrets, secret); @@ -494,6 +510,7 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) NM_SETTING (s_gsm), NM_SETTING_GSM_PASSWORD, NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); } @@ -508,6 +525,7 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) NM_SETTING (s_cdma), NM_SETTING_CDMA_PASSWORD, NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); } else if (nm_connection_is_type (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME)) { @@ -525,6 +543,7 @@ request_secrets_from_ui (NMSecretAgentSimpleRequest *request) setting, "password", NULL, + NULL, TRUE); g_ptr_array_add (secrets, secret); } else if (nm_connection_is_type (request->connection, NM_SETTING_VPN_SETTING_NAME)) { diff --git a/clients/common/nm-secret-agent-simple.h b/clients/common/nm-secret-agent-simple.h index a812cfacef..d2c58822a0 100644 --- a/clients/common/nm-secret-agent-simple.h +++ b/clients/common/nm-secret-agent-simple.h @@ -44,6 +44,7 @@ typedef struct { typedef struct { char *name, *prop_name, *value; char *vpn_property; + char *vpn_type; gboolean password; } NMSecretAgentSimpleSecret; From 348ba7645b01fe7286d7cd1ec3309d240a37bdfa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Fri, 11 Dec 2015 21:47:27 +0100 Subject: [PATCH 4/8] clients: add a helper function to get required openconnect VPN secrets OpenConnect needs three secrets - COOKIE, HOST and FINGERPRINT. They can be obtained by authenticating to the server. This can be performed by running "openconnect --authenticate " and the three values are printed to stdout. Note that the function may (probably will) interactively ask user for his credentials. Alternatively, it would be possible to dlopen() libopenconnect and call its functions. However, as that would be more complicated and would also require implementing functionality that openconnect simply does for free for us, it is not worth it, I think. (cherry picked from commit ea54b7f2c0228dff422ce05200a46fcf77f67810) --- clients/common/nm-vpn-helpers.c | 78 +++++++++++++++++++++++++++++++++ clients/common/nm-vpn-helpers.h | 7 +++ 2 files changed, 85 insertions(+) diff --git a/clients/common/nm-vpn-helpers.c b/clients/common/nm-vpn-helpers.c index 121b87a129..4a0cd23afc 100644 --- a/clients/common/nm-vpn-helpers.c +++ b/clients/common/nm-vpn-helpers.c @@ -32,6 +32,7 @@ #include +#include "nm-utils.h" #include "nm-vpn-helpers.h" @@ -268,3 +269,80 @@ nm_vpn_get_secret_names (const char *vpn_type) return NULL; } +static gboolean +_extract_variable_value (char *line, const char *tag, char **value) +{ + char *p1, *p2; + + if (g_str_has_prefix (line, tag)) { + p1 = line + strlen (tag); + p2 = line + strlen (line) - 1; + if ((*p1 == '\'' || *p1 == '"') && (*p1 == *p2)) { + p1++; + *p2 = '\0'; + } + if (value) + *value = g_strdup (p1); + return TRUE; + } + return FALSE; +} + +gboolean +nm_vpn_openconnect_authenticate_helper (const char *host, + char **cookie, + char **gateway, + char **gwcert, + int *status, + GError **error) +{ + char *output = NULL; + gboolean ret; + char **strv = NULL, **iter; + char *argv[4]; + const char *path; + const char *const DEFAULT_PATHS[] = { + "/sbin/", + "/usr/sbin/", + "/usr/local/sbin/", + "/bin/", + "/usr/bin/", + "/usr/local/bin/", + NULL, + }; + + path = nm_utils_file_search_in_paths ("openconnect", "/usr/sbin/openconnect", DEFAULT_PATHS, + G_FILE_TEST_IS_EXECUTABLE, NULL, NULL, error); + if (!path) + return FALSE; + + argv[0] = (char *) path; + argv[1] = "--authenticate"; + argv[2] = (char *) host; + argv[3] = NULL; + + ret = g_spawn_sync (NULL, argv, NULL, + G_SPAWN_SEARCH_PATH | G_SPAWN_CHILD_INHERITS_STDIN, + NULL, NULL, &output, NULL, + status, error); + + if (!ret) + return FALSE; + + /* Parse output and set cookie, gateway and gwcert + * output example: + * COOKIE='loremipsum' + * HOST='1.2.3.4' + * FINGERPRINT='sha1:32bac90cf09a722e10ecc1942c67fe2ac8c21e2e' + */ + strv = g_strsplit_set (output ? output : "", "\r\n", 0); + for (iter = strv; iter && *iter; iter++) { + _extract_variable_value (*iter, "COOKIE=", cookie); + _extract_variable_value (*iter, "HOST=", gateway); + _extract_variable_value (*iter, "FINGERPRINT=", gwcert); + } + g_strfreev (strv); + + return TRUE; +} + diff --git a/clients/common/nm-vpn-helpers.h b/clients/common/nm-vpn-helpers.h index 50e1251004..6b3396efb1 100644 --- a/clients/common/nm-vpn-helpers.h +++ b/clients/common/nm-vpn-helpers.h @@ -37,4 +37,11 @@ gboolean nm_vpn_supports_ipv6 (NMConnection *connection); const VpnPasswordName * nm_vpn_get_secret_names (const char *vpn_type); +gboolean nm_vpn_openconnect_authenticate_helper (const char *host, + char **cookie, + char **gateway, + char **gwcert, + int *status, + GError **error); + #endif /* __NM_VPN_HELPERS_H__ */ From 7acbf23b89e651355c9ef243f4576521d0f04a4e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Tue, 8 Dec 2015 08:45:45 +0100 Subject: [PATCH 5/8] cli: make asking VPN secrets for OpenConnect really work by running nm_vpn_openconnect_authenticate_helper() and filling the obtained secrets to the array sent to NM. (cherry picked from commit 45fc268890f70fec0fb66c59de388d1798837230) --- clients/cli/common.c | 96 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 95 insertions(+), 1 deletion(-) diff --git a/clients/cli/common.c b/clients/cli/common.c index 35676d0f2b..705793c5ef 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -32,6 +32,7 @@ #include "nm-glib-compat.h" +#include "nm-vpn-helpers.h" #include "common.h" #include "utils.h" @@ -926,16 +927,97 @@ nmc_find_connection (const GPtrArray *connections, return found; } +static gboolean +vpn_openconnect_get_secrets (NMConnection *connection, GPtrArray *secrets) +{ + GError *error = NULL; + NMSettingVpn *s_vpn; + const char *vpn_type, *gw, *port; + char *cookie = NULL; + char *gateway = NULL; + char *gwcert = NULL; + int status = 0; + int i; + gboolean ret; + + if (!connection) + return FALSE; + + if (!nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) + return FALSE; + + s_vpn = nm_connection_get_setting_vpn (connection); + vpn_type = nm_setting_vpn_get_service_type (s_vpn); + if (g_strcmp0 (vpn_type, NM_DBUS_INTERFACE ".openconnect")) + return FALSE; + + /* Get gateway and port */ + gw = nm_setting_vpn_get_data_item (s_vpn, "gateway"); + port = gw ? strrchr (gw, ':') : NULL; + + /* Interactively authenticate to OpenConnect server and get secrets */ + ret = nm_vpn_openconnect_authenticate_helper (gw, &cookie, &gateway, &gwcert, &status, &error); + if (!ret) { + g_printerr (_("Error: openconnect failed: %s\n"), error->message); + g_clear_error (&error); + return FALSE; + } + + if (WIFEXITED (status)) { + if (WEXITSTATUS (status) != 0) + g_printerr (_("Error: openconnect failed with status %d\n"), WEXITSTATUS (status)); + } else if (WIFSIGNALED (status)) + g_printerr (_("Error: openconnect failed with signal %d\n"), WTERMSIG (status)); + + /* Append port to the host value */ + if (gateway && port) { + char *tmp = gateway; + gateway = g_strdup_printf ("%s%s", gateway, port); + g_free (tmp); + } + + /* Fill secrets to the array */ + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; + + if (!g_strcmp0 (secret->vpn_type, vpn_type)) { + if (!g_strcmp0 (secret->vpn_property, "cookie")) { + g_free (secret->value); + secret->value = cookie; + cookie = NULL; + } else if (!g_strcmp0 (secret->vpn_property, "gateway")) { + g_free (secret->value); + secret->value = gateway; + gateway = NULL; + } else if (!g_strcmp0 (secret->vpn_property, "gwcert")) { + g_free (secret->value); + secret->value = gwcert; + gwcert = NULL; + } + } + } + g_free (cookie); + g_free (gateway); + g_free (gwcert); + + return TRUE; +} + static gboolean get_secrets_from_user (const char *request_id, const char *title, const char *msg, + NMConnection *connection, gboolean ask, GHashTable *pwds_hash, GPtrArray *secrets) { int i; + /* Check if there is a VPN OpenConnect secret to ask for */ + if (ask) + vpn_openconnect_get_secrets (connection, secrets); + for (i = 0; i < secrets->len; i++) { NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; char *pwd = NULL; @@ -993,12 +1075,24 @@ nmc_secrets_requested (NMSecretAgentSimple *agent, gpointer user_data) { NmCli *nmc = (NmCli *) user_data; + NMConnection *connection = NULL; + char *path, *p; gboolean success = FALSE; if (nmc->print_output == NMC_PRINT_PRETTY) nmc_terminal_erase_line (); - success = get_secrets_from_user (request_id, title, msg, nmc->in_editor || nmc->ask, + /* Find the connection for the request */ + path = g_strdup (request_id); + if (path) { + p = strrchr (path, '/'); + if (p) + *p = '\0'; + connection = nmc_find_connection (nmc->connections, "path", path, NULL); + g_free (path); + } + + success = get_secrets_from_user (request_id, title, msg, connection, nmc->in_editor || nmc->ask, nmc->pwds_hash, secrets); if (success) nm_secret_agent_simple_response (agent, request_id, secrets); From 19756e874d9c5c2d1fa3a7db63cef3818844e6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Fri, 11 Dec 2015 13:13:49 +0100 Subject: [PATCH 6/8] cli: do not present secrets from openconnect to the user for confirmation It is pointless to present them and require user to press Return for each password prompt. (cherry picked from commit fb62fc76cbd6ebd2807976e6ca545e465118f8d1) --- clients/cli/common.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/clients/cli/common.c b/clients/cli/common.c index 705793c5ef..86f91d5869 100644 --- a/clients/cli/common.c +++ b/clients/cli/common.c @@ -1027,17 +1027,23 @@ get_secrets_from_user (const char *request_id, if (pwds_hash && (pwd = g_hash_table_lookup (pwds_hash, secret->prop_name))) { pwd = g_strdup (pwd); } else { - g_print ("%s\n", msg); if (ask) { if (secret->value) { - /* Prefill the password if we have it. */ - rl_startup_hook = nmc_rl_set_deftext; - nmc_rl_pre_input_deftext = g_strdup (secret->value); + if (!g_strcmp0 (secret->vpn_type, NM_DBUS_INTERFACE ".openconnect")) { + /* Do not present and ask user for openconnect secrets, we already have them */ + continue; + } else { + /* Prefill the password if we have it. */ + rl_startup_hook = nmc_rl_set_deftext; + nmc_rl_pre_input_deftext = g_strdup (secret->value); + } } + g_print ("%s\n", msg); pwd = nmc_readline ("%s (%s): ", secret->name, secret->prop_name); if (!pwd) pwd = g_strdup (""); } else { + g_print ("%s\n", msg); g_printerr (_("Warning: password for '%s' not given in 'passwd-file' " "and nmcli cannot ask without '--ask' option.\n"), secret->prop_name); From 3f2dd0b2a9e15ed629ced2b7d645c99502a8e2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Fri, 11 Dec 2015 14:36:17 +0100 Subject: [PATCH 7/8] tui: allow connecting to OpenConnect by running "openconnect --authenticate" and getting secrets from it (cherry picked from commit 2e345faa7e3e45c82e536052f8ee10f9090ac8a7) --- clients/tui/nmtui-connect.c | 92 ++++++++++++++++++++++++++++++++++++- 1 file changed, 91 insertions(+), 1 deletion(-) diff --git a/clients/tui/nmtui-connect.c b/clients/tui/nmtui-connect.c index cacb0b44ea..9838b6fdfc 100644 --- a/clients/tui/nmtui-connect.c +++ b/clients/tui/nmtui-connect.c @@ -39,8 +39,60 @@ #include "nmt-connect-connection-list.h" #include "nmt-password-dialog.h" #include "nm-secret-agent-simple.h" +#include "nm-vpn-helpers.h" #include "nmt-utils.h" +/** + * Runs openconnect to authenticate. The current screen state is saved + * before starting the command and restored after it returns. + */ +static gboolean +openconnect_authenticate (NMConnection *connection, char **cookie, char **gateway, char **gwcert) +{ + GError *error = NULL; + NMSettingVpn *s_vpn; + gboolean ret; + int status = 0; + const char *gw, *port; + + nmt_newt_message_dialog (_("openconnect will be run to authenticate.\nIt will return to nmtui when completed.")); + + /* Get port */ + s_vpn = nm_connection_get_setting_vpn (connection); + gw = nm_setting_vpn_get_data_item (s_vpn, "gateway"); + port = gw ? strrchr (gw, ':') : NULL; + + newtSuspend (); + + ret = nm_vpn_openconnect_authenticate_helper (gw, cookie, gateway, gwcert, &status, &error); + + newtResume (); + + if (!ret) { + nmt_newt_message_dialog (_("Error: openconnect failed: %s"), error->message); + g_clear_error (&error); + return FALSE; + } + + if (WIFEXITED (status)) { + if (WEXITSTATUS (status) != 0) { + nmt_newt_message_dialog (_("openconnect failed with status %d"), WEXITSTATUS (status)); + return FALSE; + } + } else if (WIFSIGNALED (status)) { + nmt_newt_message_dialog (_("openconnect failed with signal %d"), WTERMSIG (status)); + return FALSE; + } + + if (gateway && *gateway && port) { + char *tmp = *gateway; + *gateway = g_strdup_printf ("%s%s", *gateway, port); + g_free (tmp); + } + + return TRUE; +} + static void secrets_requested (NMSecretAgentSimple *agent, const char *request_id, @@ -50,6 +102,44 @@ secrets_requested (NMSecretAgentSimple *agent, gpointer user_data) { NmtNewtForm *form; + NMConnection *connection = NM_CONNECTION (user_data); + char *cookie = NULL; + char *gateway = NULL; + char *gwcert = NULL; + int i; + + /* Get secrets for OpenConnect VPN */ + if (connection && nm_connection_is_type (connection, NM_SETTING_VPN_SETTING_NAME)) { + NMSettingVpn *s_vpn = nm_connection_get_setting_vpn (connection); + const char *vpn_type = nm_setting_vpn_get_service_type (s_vpn); + + if (!g_strcmp0 (vpn_type, NM_DBUS_INTERFACE ".openconnect")) { + openconnect_authenticate (connection, &cookie, &gateway, &gwcert); + + for (i = 0; i < secrets->len; i++) { + NMSecretAgentSimpleSecret *secret = secrets->pdata[i]; + + if (!g_strcmp0 (secret->vpn_type, NM_DBUS_INTERFACE ".openconnect")) { + if (!g_strcmp0 (secret->vpn_property, "cookie")) { + g_free (secret->value); + secret->value = cookie; + cookie = NULL; + } else if (!g_strcmp0 (secret->vpn_property, "gateway")) { + g_free (secret->value); + secret->value = gateway; + gateway = NULL; + } else if (!g_strcmp0 (secret->vpn_property, "gwcert")) { + g_free (secret->value); + secret->value = gwcert; + gwcert = NULL; + } + } + } + g_free (cookie); + g_free (gateway); + g_free (gwcert); + } + } form = nmt_password_dialog_new (request_id, title, msg, secrets); nmt_newt_form_run_sync (form); @@ -153,7 +243,7 @@ activate_connection (NMConnection *connection, nm_secret_agent_simple_enable (NM_SECRET_AGENT_SIMPLE (agent), nm_object_get_path (NM_OBJECT (connection))); } - g_signal_connect (agent, "request-secrets", G_CALLBACK (secrets_requested), NULL); + g_signal_connect (agent, "request-secrets", G_CALLBACK (secrets_requested), connection); } specific_object_path = specific_object ? nm_object_get_path (specific_object) : NULL; From 71cfa4fb7948389de61408c53cb13d693d3c41a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Fri, 11 Dec 2015 15:14:59 +0100 Subject: [PATCH 8/8] tui: prefill a secret in the entry if we have a value (cherry picked from commit a1536fe3d1ca85f321b6498d884a38eacf02de90) --- clients/tui/nmt-password-dialog.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/clients/tui/nmt-password-dialog.c b/clients/tui/nmt-password-dialog.c index aeece1211f..bf29dcf5a1 100644 --- a/clients/tui/nmt-password-dialog.c +++ b/clients/tui/nmt-password-dialog.c @@ -154,6 +154,8 @@ nmt_password_dialog_constructed (GObject *object) if (secret->password) flags |= NMT_NEWT_ENTRY_PASSWORD; widget = nmt_newt_entry_new (30, flags); + if (secret->value) + nmt_newt_entry_set_text (NMT_NEWT_ENTRY (widget), secret->value); nmt_newt_grid_add (secret_grid, widget, 1, i); g_ptr_array_add (priv->entries, widget);