Files
NetworkManager/clients/common/nm-secret-agent-simple.c
Thomas Haller abdf9a3673 all: change handling of connection.type for bluetooth NAP and in general
Branch f9b1bc16e9 added bluetooth NAP
support. A NAP connection is of connection.type "bluetooth", but it
also has a "bridge" setting. Also, it is primarily handled by NMDeviceBridge
and NMBridgeDeviceFactory (with help from NMBluezManager).

However, don't let nm_connection_get_connection_type() and
nm_connnection_is_type() lie about what the connection.type is.
The type is "bluetooth" for most purposes -- at least, as far as
the client is concerned (and the public API of libnm). This restores
previous API behavior, where nm_connection_get_connection_type()
and nm_connection_is_type() would be simple accessors to the
"connection.type" property.

Only a few places care about the bridge aspect, and those places need special
treatment. For example NMDeviceBridge needs to be fully aware that it can
handle bluetooth NAP connection. That is nothing new: if you handle a
connection of any type, you must know which fields matter and what they
mean. It's not enough that nm_connection_get_connection_type() for bluetooth
NAP connectins is claiming to be a bridge.

Counter examples, where the original behavior is right:

src/nm-manager.c-        g_set_error (error,
src/nm-manager.c-                     NM_MANAGER_ERROR,
src/nm-manager.c-                     NM_MANAGER_ERROR_FAILED,
src/nm-manager.c-                     "NetworkManager plugin for '%s' unavailable",
src/nm-manager.c:                     nm_connection_get_connection_type (connection));

the correct message is: "no bluetooth plugin available", not "bridge".

src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c:   if (   (   nm_connection_is_type (connection, NM_SETTING_WIRED_SETTING_NAME)
src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c:           && !nm_connection_get_setting_pppoe (connection))
src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c:       || nm_connection_is_type (connection, NM_SETTING_VLAN_SETTING_NAME)
src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c:       || nm_connection_is_type (connection, NM_SETTING_WIRELESS_SETTING_NAME)
src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c:       || nm_connection_is_type (connection, NM_SETTING_INFINIBAND_SETTING_NAME)
src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c:       || nm_connection_is_type (connection, NM_SETTING_BOND_SETTING_NAME)
src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c:       || nm_connection_is_type (connection, NM_SETTING_TEAM_SETTING_NAME)
src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c:       || nm_connection_is_type (connection, NM_SETTING_BRIDGE_SETTING_NAME))
src/settings/plugins/ifcfg-rh/nms-ifcfg-rh-writer.c-        return TRUE;

the correct behavior is for ifcfg-rh plugin to reject bluetooth NAP
connections, not proceed and store it.
2017-06-07 09:07:17 +02:00

881 lines
32 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/*
* 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, see <http://www.gnu.org/licenses/>.
*
* Copyright 2011-2015 Red Hat, Inc.
* Copyright 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
*/
/**
* SECTION:nm-secret-agent-simple
* @short_description: A simple secret agent for NetworkManager
*
* #NMSecretAgentSimple is the secret agent used by nmtui-connect and nmcli.
*
* This is a stripped-down version of gnome-shell's ShellNetworkAgent,
* with bits of the corresponding JavaScript code squished down into
* it. It is intended to eventually be generic enough that it could
* replace ShellNetworkAgent.
*/
#include "nm-default.h"
#include <string.h>
#include "NetworkManager.h"
#include "nm-vpn-service-plugin.h"
#include "nm-vpn-helpers.h"
#include "nm-secret-agent-simple.h"
G_DEFINE_TYPE (NMSecretAgentSimple, nm_secret_agent_simple, NM_TYPE_SECRET_AGENT_OLD)
#define NM_SECRET_AGENT_SIMPLE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT_SIMPLE, NMSecretAgentSimplePrivate))
enum {
REQUEST_SECRETS,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
typedef struct {
NMSecretAgentSimple *self;
gchar *request_id;
NMConnection *connection;
gchar **hints;
NMSecretAgentOldGetSecretsFunc callback;
gpointer callback_data;
} NMSecretAgentSimpleRequest;
typedef struct {
/* <char *request_id, NMSecretAgentSimpleRequest *request> */
GHashTable *requests;
char *path;
gboolean enabled;
} NMSecretAgentSimplePrivate;
static void
nm_secret_agent_simple_request_free (gpointer data)
{
NMSecretAgentSimpleRequest *request = data;
g_object_unref (request->self);
g_object_unref (request->connection);
g_strfreev (request->hints);
g_slice_free (NMSecretAgentSimpleRequest, request);
}
static void
nm_secret_agent_simple_init (NMSecretAgentSimple *agent)
{
NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (agent);
priv->requests = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, nm_secret_agent_simple_request_free);
}
static void
nm_secret_agent_simple_finalize (GObject *object)
{
NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (object);
GError *error;
GHashTableIter iter;
gpointer key;
gpointer value;
error = g_error_new (NM_SECRET_AGENT_ERROR,
NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
"The secret agent is going away");
g_hash_table_iter_init (&iter, priv->requests);
while (g_hash_table_iter_next (&iter, &key, &value)) {
NMSecretAgentSimpleRequest *request = value;
request->callback (NM_SECRET_AGENT_OLD (object),
request->connection,
NULL, error,
request->callback_data);
}
g_hash_table_destroy (priv->requests);
g_error_free (error);
g_free (priv->path);
G_OBJECT_CLASS (nm_secret_agent_simple_parent_class)->finalize (object);
}
static gboolean
strv_has (gchar **haystack,
gchar *needle)
{
gchar **iter;
for (iter = haystack; iter && *iter; iter++) {
if (g_strcmp0 (*iter, needle) == 0)
return TRUE;
}
return FALSE;
}
/**
* NMSecretAgentSimpleSecret:
* @name: the user-visible name of the secret. Eg, "WEP Passphrase".
* @value: the value of the secret
* @password: %TRUE if this secret represents a password, %FALSE
* if it represents non-secret data.
*
* A single "secret" being requested.
*/
typedef struct {
NMSecretAgentSimpleSecret base;
NMSetting *setting;
char *property;
} NMSecretAgentSimpleSecretReal;
static void
nm_secret_agent_simple_secret_free (NMSecretAgentSimpleSecret *secret)
{
NMSecretAgentSimpleSecretReal *real = (NMSecretAgentSimpleSecretReal *)secret;
g_free (secret->name);
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);
g_slice_free (NMSecretAgentSimpleSecretReal, real);
}
static NMSecretAgentSimpleSecret *
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;
real = g_slice_new0 (NMSecretAgentSimpleSecretReal);
real->base.name = g_strdup (name);
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.vpn_type = g_strdup (vpn_type);
real->base.password = password;
if (setting) {
real->setting = g_object_ref (setting);
real->property = g_strdup (property);
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;
}
static gboolean
add_8021x_secrets (NMSecretAgentSimpleRequest *request,
GPtrArray *secrets)
{
NMSetting8021x *s_8021x = nm_connection_get_setting_802_1x (request->connection);
const char *eap_method;
NMSecretAgentSimpleSecret *secret;
eap_method = nm_setting_802_1x_get_eap_method (s_8021x, 0);
if (!eap_method)
return FALSE;
if ( !strcmp (eap_method, "md5")
|| !strcmp (eap_method, "leap")
|| !strcmp (eap_method, "ttls")
|| !strcmp (eap_method, "peap")) {
/* TTLS and PEAP are actually much more complicated, but this complication
* is not visible here since we only care about phase2 authentication
* (and don't even care of which one)
*/
secret = nm_secret_agent_simple_secret_new (_("Username"),
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;
}
if (!strcmp (eap_method, "tls")) {
secret = nm_secret_agent_simple_secret_new (_("Identity"),
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;
}
return FALSE;
}
static gboolean
add_wireless_secrets (NMSecretAgentSimpleRequest *request,
GPtrArray *secrets)
{
NMSettingWirelessSecurity *s_wsec = nm_connection_get_setting_wireless_security (request->connection);
const char *key_mgmt = nm_setting_wireless_security_get_key_mgmt (s_wsec);
NMSecretAgentSimpleSecret *secret;
if (!key_mgmt)
return FALSE;
if (!strcmp (key_mgmt, "wpa-none") || !strcmp (key_mgmt, "wpa-psk")) {
secret = nm_secret_agent_simple_secret_new (_("Password"),
NM_SETTING (s_wsec),
NM_SETTING_WIRELESS_SECURITY_PSK,
NULL,
NULL,
TRUE);
g_ptr_array_add (secrets, secret);
return TRUE;
}
if (!strcmp (key_mgmt, "none")) {
int index;
char *key;
index = nm_setting_wireless_security_get_wep_tx_keyidx (s_wsec);
key = g_strdup_printf ("wep-key%d", index);
secret = nm_secret_agent_simple_secret_new (_("Key"),
NM_SETTING (s_wsec),
key,
NULL,
NULL,
TRUE);
g_free (key);
g_ptr_array_add (secrets, secret);
return TRUE;
}
if (!strcmp (key_mgmt, "iee8021x")) {
if (!g_strcmp0 (nm_setting_wireless_security_get_auth_alg (s_wsec), "leap")) {
secret = nm_secret_agent_simple_secret_new (_("Password"),
NM_SETTING (s_wsec),
NM_SETTING_WIRELESS_SECURITY_LEAP_PASSWORD,
NULL,
NULL,
TRUE);
g_ptr_array_add (secrets, secret);
return TRUE;
} else
return add_8021x_secrets (request, secrets);
}
if (!strcmp (key_mgmt, "wpa-eap"))
return add_8021x_secrets (request, secrets);
return FALSE;
}
static gboolean
add_pppoe_secrets (NMSecretAgentSimpleRequest *request,
GPtrArray *secrets)
{
NMSettingPppoe *s_pppoe = nm_connection_get_setting_pppoe (request->connection);
NMSecretAgentSimpleSecret *secret;
secret = nm_secret_agent_simple_secret_new (_("Username"),
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;
}
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,
nm_setting_vpn_get_service_type (s_vpn),
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;
const char *vpn_msg = NULL;
char **iter;
/* If hints are given, then always ask for what the hints require */
if (request->hints) {
for (iter = request->hints; *iter; iter++) {
if (!vpn_msg && g_str_has_prefix (*iter, VPN_MSG_TAG))
vpn_msg = &(*iter)[NM_STRLEN (VPN_MSG_TAG)];
else
add_vpn_secret_helper (secrets, s_vpn, *iter, *iter);
}
}
NM_SET_OUT (msg, g_strdup (vpn_msg));
/* Now add what client thinks might be required, because hints may be empty or incomplete */
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++;
}
return TRUE;
}
static void
request_secrets_from_ui (NMSecretAgentSimpleRequest *request)
{
GPtrArray *secrets;
NMSecretAgentSimplePrivate *priv;
NMSecretAgentSimpleSecret *secret;
const char *title;
char *msg;
gboolean ok = TRUE;
priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (request->self);
g_return_if_fail (priv->enabled);
/* We only handle requests for connection with @path if set. */
if (priv->path && !g_str_has_prefix (request->request_id, priv->path)) {
gs_free_error GError *error = NULL;
error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
"Request for %s secrets doesn't match path %s",
request->request_id, priv->path);
request->callback (NM_SECRET_AGENT_OLD (request->self), request->connection,
NULL, error, request->callback_data);
g_hash_table_remove (priv->requests, request->request_id);
return;
}
secrets = g_ptr_array_new_with_free_func ((GDestroyNotify) nm_secret_agent_simple_secret_free);
if (nm_connection_is_type (request->connection, NM_SETTING_WIRELESS_SETTING_NAME)) {
NMSettingWireless *s_wireless;
GBytes *ssid;
char *ssid_utf8;
s_wireless = nm_connection_get_setting_wireless (request->connection);
ssid = nm_setting_wireless_get_ssid (s_wireless);
ssid_utf8 = nm_utils_ssid_to_utf8 (g_bytes_get_data (ssid, NULL),
g_bytes_get_size (ssid));
title = _("Authentication required by wireless network");
msg = g_strdup_printf (_("Passwords or encryption keys are required to access the wireless network '%s'."), ssid_utf8);
ok = add_wireless_secrets (request, secrets);
} else if (nm_connection_is_type (request->connection, NM_SETTING_WIRED_SETTING_NAME)) {
NMSettingConnection *s_con;
s_con = nm_connection_get_setting_connection (request->connection);
title = _("Wired 802.1X authentication");
msg = g_strdup_printf (_("Secrets are required to access the wired network '%s'"),
nm_connection_get_id (request->connection));
ok = add_8021x_secrets (request, secrets);
} else if (nm_connection_is_type (request->connection, NM_SETTING_PPPOE_SETTING_NAME)) {
title = _("DSL authentication");
msg = g_strdup_printf (_("Secrets are required for the DSL connection '%s'"),
nm_connection_get_id (request->connection));
ok = add_pppoe_secrets (request, secrets);
} else if (nm_connection_is_type (request->connection, NM_SETTING_GSM_SETTING_NAME)) {
NMSettingGsm *s_gsm = nm_connection_get_setting_gsm (request->connection);
if (strv_has (request->hints, "pin")) {
title = _("PIN code required");
msg = g_strdup (_("PIN code is needed for the mobile broadband device"));
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);
} else {
title = _("Mobile broadband network password");
msg = g_strdup_printf (_("A password is required to connect to '%s'."),
nm_connection_get_id (request->connection));
secret = nm_secret_agent_simple_secret_new (_("Password"),
NM_SETTING (s_gsm),
NM_SETTING_GSM_PASSWORD,
NULL,
NULL,
TRUE);
g_ptr_array_add (secrets, secret);
}
} else if (nm_connection_is_type (request->connection, NM_SETTING_MACSEC_SETTING_NAME)) {
NMSettingMacsec *s_macsec = nm_connection_get_setting_macsec (request->connection);
msg = g_strdup_printf (_("Secrets are required to access the MACsec network '%s'"),
nm_connection_get_id (request->connection));
if (nm_setting_macsec_get_mode (s_macsec) == NM_SETTING_MACSEC_MODE_PSK) {
title = _("MACsec PSK authentication");
secret = nm_secret_agent_simple_secret_new (_("MKA CAK"),
NM_SETTING (s_macsec),
NM_SETTING_MACSEC_MKA_CAK,
NULL,
NULL,
TRUE);
g_ptr_array_add (secrets, secret);
} else {
title = _("MACsec EAP authentication");
ok = add_8021x_secrets (request, secrets);
}
} else if (nm_connection_is_type (request->connection, NM_SETTING_CDMA_SETTING_NAME)) {
NMSettingCdma *s_cdma = nm_connection_get_setting_cdma (request->connection);
title = _("Mobile broadband network password");
msg = g_strdup_printf (_("A password is required to connect to '%s'."),
nm_connection_get_id (request->connection));
secret = nm_secret_agent_simple_secret_new (_("Password"),
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)) {
NMSetting *setting = NULL;
setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_BLUETOOTH_SETTING_NAME);
if ( setting
&& !nm_streq0 (nm_setting_bluetooth_get_connection_type (NM_SETTING_BLUETOOTH (setting)), NM_SETTING_BLUETOOTH_TYPE_NAP)) {
setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_GSM_SETTING_NAME);
if (!setting)
setting = nm_connection_get_setting_by_name (request->connection, NM_SETTING_CDMA_SETTING_NAME);
}
if (setting) {
title = _("Mobile broadband network password");
msg = g_strdup_printf (_("A password is required to connect to '%s'."),
nm_connection_get_id (request->connection));
secret = nm_secret_agent_simple_secret_new (_("Password"),
setting,
"password",
NULL,
NULL,
TRUE);
g_ptr_array_add (secrets, secret);
} else
ok = FALSE;
} 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;
if (!ok) {
gs_free_error GError *error = NULL;
error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
"Cannot service a secrets request %s for a %s connection",
request->request_id,
nm_connection_get_connection_type (request->connection));
request->callback (NM_SECRET_AGENT_OLD (request->self), request->connection,
NULL, error, request->callback_data);
g_hash_table_remove (priv->requests, request->request_id);
g_ptr_array_unref (secrets);
return;
}
g_signal_emit (request->self, signals[REQUEST_SECRETS], 0,
request->request_id, title, msg, secrets);
}
static void
nm_secret_agent_simple_get_secrets (NMSecretAgentOld *agent,
NMConnection *connection,
const gchar *connection_path,
const gchar *setting_name,
const gchar **hints,
NMSecretAgentGetSecretsFlags flags,
NMSecretAgentOldGetSecretsFunc callback,
gpointer callback_data)
{
NMSecretAgentSimple *self = NM_SECRET_AGENT_SIMPLE (agent);
NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self);
NMSecretAgentSimpleRequest *request;
NMSettingConnection *s_con;
const char *connection_type;
char *request_id;
GError *error;
request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
if (g_hash_table_lookup (priv->requests, request_id) != NULL) {
/* We already have a request pending for this (connection, setting) */
error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED,
"Request for %s secrets already pending", request_id);
nope:
callback (agent, connection, NULL, error, callback_data);
g_error_free (error);
g_free (request_id);
return;
}
s_con = nm_connection_get_setting_connection (connection);
connection_type = nm_setting_connection_get_connection_type (s_con);
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,
"Stored passwords not supported");
goto nope;
}
request = g_slice_new (NMSecretAgentSimpleRequest);
request->self = g_object_ref (self);
request->connection = g_object_ref (connection);
request->hints = g_strdupv ((gchar **)hints);
request->callback = callback;
request->callback_data = callback_data;
request->request_id = request_id;
g_hash_table_replace (priv->requests, request->request_id, request);
if (priv->enabled)
request_secrets_from_ui (request);
}
/**
* nm_secret_agent_simple_response:
* @self: the #NMSecretAgentSimple
* @request_id: the request ID being responded to
* @secrets: (allow-none): the array of secrets, or %NULL
*
* Response to a #NMSecretAgentSimple::get-secrets signal.
*
* If the user provided secrets, the caller should set the
* corresponding <literal>value</literal> fields in the
* #NMSecretAgentSimpleSecrets (freeing any initial values they had), and
* pass the array to nm_secret_agent_simple_response(). If the user
* cancelled the request, @secrets should be NULL.
*/
void
nm_secret_agent_simple_response (NMSecretAgentSimple *self,
const char *request_id,
GPtrArray *secrets)
{
NMSecretAgentSimplePrivate *priv;
NMSecretAgentSimpleRequest *request;
GVariant *dict = NULL;
GError *error = NULL;
int i;
g_return_if_fail (NM_IS_SECRET_AGENT_SIMPLE (self));
priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self);
request = g_hash_table_lookup (priv->requests, request_id);
g_return_if_fail (request != NULL);
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++) {
NMSecretAgentSimpleSecretReal *secret = secrets->pdata[i];
setting_builder = g_hash_table_lookup (settings, nm_setting_get_name (secret->setting));
if (!setting_builder) {
setting_builder = g_variant_builder_new (NM_VARIANT_TYPE_SETTING);
g_hash_table_insert (settings, (char *) nm_setting_get_name (secret->setting),
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}",
vpn_secrets_base_name,
g_variant_builder_end (&vpn_secrets_builder));
}
g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION);
g_hash_table_iter_init (&iter, settings);
while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &setting_builder))
g_variant_builder_add (&conn_builder, "{sa{sv}}", name, setting_builder);
dict = g_variant_builder_end (&conn_builder);
g_hash_table_destroy (settings);
} else {
error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED,
"User cancelled");
}
request->callback (NM_SECRET_AGENT_OLD (self), request->connection, dict, error, request->callback_data);
g_clear_error (&error);
g_hash_table_remove (priv->requests, request_id);
}
static void
nm_secret_agent_simple_cancel_get_secrets (NMSecretAgentOld *agent,
const gchar *connection_path,
const gchar *setting_name)
{
/* We don't support cancellation. Sorry! */
}
static void
nm_secret_agent_simple_save_secrets (NMSecretAgentOld *agent,
NMConnection *connection,
const gchar *connection_path,
NMSecretAgentOldSaveSecretsFunc callback,
gpointer callback_data)
{
/* We don't support secret storage */
callback (agent, connection, NULL, callback_data);
}
static void
nm_secret_agent_simple_delete_secrets (NMSecretAgentOld *agent,
NMConnection *connection,
const gchar *connection_path,
NMSecretAgentOldDeleteSecretsFunc callback,
gpointer callback_data)
{
/* We don't support secret storage, so there's nothing to delete. */
callback (agent, connection, NULL, callback_data);
}
/**
* nm_secret_agent_simple_enable:
* @self: the #NMSecretAgentSimple
* @path: (allow-none): the path of the connection (if any) to handle secrets
* for. If %NULL, secrets for any connection will be handled.
*
* Enables servicing the requests including the already queued ones. If @path
* is given, the agent will only handle requests for connections that match
* @path.
*/
void
nm_secret_agent_simple_enable (NMSecretAgentSimple *self, const char *path)
{
NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self);
GList *requests, *iter;
gs_free char *path_full = NULL;
/* The path is only used to match a request_id with the current
* connection. Since the request_id is "${CONNECTION_PATH}/${SETTING}",
* add a trailing '/' to the path to match the full connection path.
*/
path_full = path ? g_strdup_printf ("%s/", path) : NULL;
if (g_strcmp0 (path_full, priv->path) != 0) {
g_free (priv->path);
priv->path = path_full;
path_full = NULL;
}
if (priv->enabled)
return;
priv->enabled = TRUE;
/* Service pending secret requests. */
requests = g_hash_table_get_values (priv->requests);
for (iter = requests; iter; iter = g_list_next (iter))
request_secrets_from_ui (iter->data);
g_list_free (requests);
}
void
nm_secret_agent_simple_class_init (NMSecretAgentSimpleClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
NMSecretAgentOldClass *agent_class = NM_SECRET_AGENT_OLD_CLASS (klass);
g_type_class_add_private (klass, sizeof (NMSecretAgentSimplePrivate));
gobject_class->finalize = nm_secret_agent_simple_finalize;
agent_class->get_secrets = nm_secret_agent_simple_get_secrets;
agent_class->cancel_get_secrets = nm_secret_agent_simple_cancel_get_secrets;
agent_class->save_secrets = nm_secret_agent_simple_save_secrets;
agent_class->delete_secrets = nm_secret_agent_simple_delete_secrets;
/**
* NMSecretAgentSimple::request-secrets:
* @agent: the #NMSecretAgentSimple
* @request_id: request ID, to eventually pass to
* nm_secret_agent_simple_response().
* @title: a title for the password dialog
* @prompt: a prompt message for the password dialog
* @secrets: (element-type #NMSecretAgentSimpleSecret): array of secrets
* being requested.
*
* Emitted when the agent requires secrets from the user.
*
* The application should ask user for the secrets. For example,
* nmtui should create a password dialog (#NmtPasswordDialog)
* with the given title and prompt, and an entry for each
* element of @secrets. If any of the secrets already have a
* <literal>value</literal> filled in, the corresponding entry
* should be initialized to that value.
*
* When the dialog is complete, the app must call
* nm_secret_agent_simple_response() with the results.
*/
signals[REQUEST_SECRETS] = g_signal_new (NM_SECRET_AGENT_SIMPLE_REQUEST_SECRETS,
G_TYPE_FROM_CLASS (klass),
0, 0, NULL, NULL, NULL,
G_TYPE_NONE,
4,
G_TYPE_STRING, /* request_id */
G_TYPE_STRING, /* title */
G_TYPE_STRING, /* prompt */
G_TYPE_PTR_ARRAY);
}
/**
* nm_secret_agent_simple_new:
* @name: the identifier of secret agent
*
* Creates a new #NMSecretAgentSimple. It does not serve any requests until
* nm_secret_agent_simple_enable() is called.
*
* Returns: a new #NMSecretAgentSimple if the agent creation is successful
* or %NULL in case of a failure.
*/
NMSecretAgentOld *
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);
}