1
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
3
* Dan Williams <dcbw@redhat.com>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License along
16
* with this program; if not, write to the Free Software Foundation, Inc.,
17
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19
* Copyright (C) 2011 Red Hat, Inc.
26
#include <glib/gi18n.h>
28
#include <gnome-keyring.h>
29
#include <dbus/dbus-glib.h>
30
#include <nm-setting-connection.h>
31
#include <nm-setting-8021x.h>
32
#include <nm-setting-vpn.h>
33
#include <nm-setting-wireless.h>
34
#include <nm-setting-wireless-security.h>
35
#include <nm-setting-wired.h>
36
#include <nm-setting-pppoe.h>
38
#include "applet-agent.h"
40
#include "nma-marshal.h"
42
G_DEFINE_TYPE (AppletAgent, applet_agent, NM_TYPE_SECRET_AGENT);
44
#define APPLET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), APPLET_TYPE_AGENT, AppletAgentPrivate))
57
static guint signals[LAST_SIGNAL] = { 0 };
60
/*******************************************************/
62
#define DBUS_TYPE_G_MAP_OF_STRING (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING))
68
NMConnection *connection;
73
NMSecretAgentGetSecretsFunc get_callback;
74
NMSecretAgentSaveSecretsFunc save_callback;
75
NMSecretAgentDeleteSecretsFunc delete_callback;
76
gpointer callback_data;
84
request_new (NMSecretAgent *agent,
85
NMConnection *connection,
86
const char *connection_path,
87
const char *setting_name,
90
NMSecretAgentGetSecretsFunc get_callback,
91
NMSecretAgentSaveSecretsFunc save_callback,
92
NMSecretAgentDeleteSecretsFunc delete_callback,
93
gpointer callback_data)
95
static guint32 counter = 1;
98
r = g_slice_new0 (Request);
101
r->connection = g_object_ref (connection);
102
r->path = g_strdup (connection_path);
103
r->setting_name = g_strdup (setting_name);
105
r->hints = g_strdupv ((gchar **) hints);
107
r->get_callback = get_callback;
108
r->save_callback = save_callback;
109
r->delete_callback = delete_callback;
110
r->callback_data = callback_data;
115
request_free (Request *r)
117
if (r->canceled == FALSE)
118
g_hash_table_remove (APPLET_AGENT_GET_PRIVATE (r->agent)->requests, GUINT_TO_POINTER (r->id));
120
g_object_unref (r->connection);
121
g_slist_free (r->keyring_ids);
123
g_free (r->setting_name);
124
g_strfreev (r->hints);
125
memset (r, 0, sizeof (*r));
126
g_slice_free (Request, r);
130
/*************************************************************/
132
/* The keyring doesn't pass the call ID to the callback for the
133
* operation which the call ID represents, so we have to track
134
* it with a small structure. Ugh.
142
static inline KeyringCall *
143
keyring_call_new (Request *r)
147
call = g_malloc0 (sizeof (KeyringCall));
153
keyring_call_free (gpointer data)
155
KeyringCall *call = data;
157
memset (call, 0, sizeof (*call));
161
/*******************************************************/
164
get_secrets_cb (AppletAgent *self,
169
Request *r = user_data;
171
if (r->canceled == FALSE)
172
r->get_callback (NM_SECRET_AGENT (r->agent), r->connection, error ? NULL : secrets, error, r->callback_data);
177
ask_for_secrets (Request *r)
179
/* Ask the applet to get some secrets for us */
180
g_signal_emit (r->agent,
181
signals[GET_SECRETS],
183
GUINT_TO_POINTER (r->id),
193
check_always_ask_cb (NMSetting *setting,
199
gboolean *always_ask = user_data;
200
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
202
if (flags & NM_SETTING_PARAM_SECRET) {
203
if (nm_setting_get_secret_flags (setting, key, &secret_flags, NULL)) {
204
if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
211
has_always_ask (NMSetting *setting)
213
gboolean always_ask = FALSE;
215
nm_setting_enumerate_values (setting, check_always_ask_cb, &always_ask);
220
is_connection_always_ask (NMConnection *connection)
222
NMSettingConnection *s_con;
226
/* For the given connection type, check if the secrets for that connection
227
* are always-ask or not.
229
s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
231
ctype = nm_setting_connection_get_connection_type (s_con);
233
setting = nm_connection_get_setting_by_name (connection, ctype);
234
g_return_val_if_fail (setting != NULL, FALSE);
236
if (has_always_ask (setting))
239
/* Try type-specific settings too; be a bit paranoid and only consider
240
* secrets from settings relevant to the connection type.
242
if (NM_IS_SETTING_WIRELESS (setting)) {
243
setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
244
if (setting && has_always_ask (setting))
246
setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
247
if (setting && has_always_ask (setting))
249
} else if (NM_IS_SETTING_WIRED (setting)) {
250
setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
251
if (setting && has_always_ask (setting))
253
setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
254
if (setting && has_always_ask (setting))
262
string_to_gvalue (const char *str)
266
val = g_slice_new0 (GValue);
267
g_value_init (val, G_TYPE_STRING);
268
g_value_set_string (val, str);
273
destroy_gvalue (gpointer data)
275
g_value_unset ((GValue *) data);
276
g_slice_free (GValue, data);
280
keyring_find_secrets_cb (GnomeKeyringResult result,
284
KeyringCall *call = user_data;
285
Request *r = call->r;
286
GError *error = NULL;
287
NMSettingConnection *s_con;
288
const char *connection_id = NULL;
289
GHashTable *secrets = NULL, *settings = NULL;
291
gboolean hint_found = FALSE, ask = FALSE;
293
r->keyring_ids = g_slist_remove (r->keyring_ids, call->keyring_id);
296
/* Callback already called by cancelation handler */
301
s_con = (NMSettingConnection *) nm_connection_get_setting (r->connection, NM_TYPE_SETTING_CONNECTION);
303
connection_id = nm_setting_connection_get_id (s_con);
305
if (result == GNOME_KEYRING_RESULT_CANCELLED) {
306
error = g_error_new_literal (NM_SECRET_AGENT_ERROR,
307
NM_SECRET_AGENT_ERROR_USER_CANCELED,
308
"The secrets request was canceled by the user");
310
} else if ( result != GNOME_KEYRING_RESULT_OK
311
&& result != GNOME_KEYRING_RESULT_NO_MATCH) {
312
error = g_error_new (NM_SECRET_AGENT_ERROR,
313
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
314
"%s.%d - failed to read secrets from keyring (result %d)",
315
__FILE__, __LINE__, result);
319
/* Only ask if we're allowed to, ie if flags != NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE */
320
if (r->flags && g_list_length (list) == 0) {
321
g_message ("No keyring secrets found for %s/%s; asking user.", connection_id, r->setting_name);
326
secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, destroy_gvalue);
328
/* Extract the secrets from the list of matching keyring items */
329
for (iter = list; iter != NULL; iter = g_list_next (iter)) {
330
GnomeKeyringFound *found = iter->data;
331
GnomeKeyringAttribute *attr;
332
const char *key_name = NULL;
335
for (i = 0; i < found->attributes->len; i++) {
336
attr = &(gnome_keyring_attribute_list_index (found->attributes, i));
337
if ( (strcmp (attr->name, KEYRING_SK_TAG) == 0)
338
&& (attr->type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING)) {
340
key_name = attr->value.string;
341
g_hash_table_insert (secrets, g_strdup (key_name), string_to_gvalue (found->secret));
343
/* See if this property matches a given hint */
344
if (r->hints && r->hints[0]) {
345
if (!g_strcmp0 (r->hints[0], key_name) || !g_strcmp0 (r->hints[1], key_name))
353
/* If there were hints, and none of the hints were returned by the keyring,
354
* get some new secrets.
357
if (r->hints && r->hints[0] && !hint_found)
359
else if (r->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) {
360
g_message ("New secrets for %s/%s requested; ask the user", connection_id, r->setting_name);
362
} else if ( (r->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION)
363
&& is_connection_always_ask (r->connection))
367
/* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that
368
* will contain all the individual settings hashes.
370
settings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_hash_table_destroy);
371
g_hash_table_insert (settings, g_strdup (r->setting_name), secrets);
375
GHashTableIter hash_iter;
376
const char *setting_name;
377
GHashTable *setting_hash;
379
/* Stuff all the found secrets into the connection for the UI to use */
380
g_hash_table_iter_init (&hash_iter, settings);
381
while (g_hash_table_iter_next (&hash_iter,
382
(gpointer *) &setting_name,
383
(gpointer *) &setting_hash)) {
384
nm_connection_update_secrets (r->connection,
392
/* Otherwise send the secrets back to NetworkManager */
393
r->get_callback (NM_SECRET_AGENT (r->agent), r->connection, error ? NULL : settings, error, r->callback_data);
398
g_hash_table_destroy (settings);
399
g_clear_error (&error);
403
get_secrets (NMSecretAgent *agent,
404
NMConnection *connection,
405
const char *connection_path,
406
const char *setting_name,
409
NMSecretAgentGetSecretsFunc callback,
410
gpointer callback_data)
412
AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
414
GError *error = NULL;
415
NMSettingConnection *s_con;
421
setting = nm_connection_get_setting_by_name (connection, setting_name);
423
error = g_error_new (NM_SECRET_AGENT_ERROR,
424
NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
425
"%s.%d - Connection didn't have requested setting '%s'.",
426
__FILE__, __LINE__, setting_name);
427
callback (agent, connection, NULL, error, callback_data);
428
g_error_free (error);
432
s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
434
id = nm_setting_connection_get_id (s_con);
435
ctype = nm_setting_connection_get_connection_type (s_con);
437
if (!s_con || !id || !strlen (id) || !ctype) {
438
error = g_error_new (NM_SECRET_AGENT_ERROR,
439
NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
440
"%s.%d - Connection didn't have required '"
441
NM_SETTING_CONNECTION_SETTING_NAME
442
"' setting , or the connection name was invalid.",
444
callback (agent, connection, NULL, error, callback_data);
445
g_error_free (error);
449
/* Track the secrets request */
450
r = request_new (agent, connection, connection_path, setting_name, hints, flags, callback, NULL, NULL, callback_data);
451
g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r);
453
/* VPN passwords are handled by the VPN plugin's auth dialog */
454
if (!strcmp (ctype, NM_SETTING_VPN_SETTING_NAME)) {
459
/* For everything else we scrape the keyring for secrets first, and ask
462
call = keyring_call_new (r);
463
call->keyring_id = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
464
keyring_find_secrets_cb,
468
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
469
nm_setting_connection_get_uuid (s_con),
471
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
476
/*******************************************************/
479
cancel_get_secrets (NMSecretAgent *agent,
480
const char *connection_path,
481
const char *setting_name)
483
AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
488
error = g_error_new_literal (NM_SECRET_AGENT_ERROR,
489
NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
490
"Canceled by NetworkManager");
492
g_hash_table_iter_init (&iter, priv->requests);
493
while (g_hash_table_iter_next (&iter, NULL, &data)) {
496
if (r->get_callback == NULL)
499
/* Cancel any matching GetSecrets call */
500
if (!g_strcmp0 (r->path, connection_path) && !g_strcmp0 (r->setting_name, setting_name)) {
503
/* cancel outstanding keyring operations */
504
for (kiter = r->keyring_ids; kiter; kiter = g_slist_next (kiter))
505
gnome_keyring_cancel_request (kiter->data);
506
g_slist_free (r->keyring_ids);
507
r->keyring_ids = NULL;
510
r->get_callback (NM_SECRET_AGENT (r->agent), r->connection, NULL, error, r->callback_data);
511
g_hash_table_remove (priv->requests, GUINT_TO_POINTER (r->id));
512
g_signal_emit (r->agent, signals[CANCEL_SECRETS], 0, GUINT_TO_POINTER (r->id));
516
g_error_free (error);
519
/*******************************************************/
522
save_secret_cb (GnomeKeyringResult result, guint val, gpointer user_data)
524
KeyringCall *call = user_data;
525
Request *r = call->r;
527
r->keyring_ids = g_slist_remove (r->keyring_ids, call->keyring_id);
529
/* Only call the SaveSecrets callback and free the request when all the
530
* secrets have been saved to the keyring.
533
if (r->op_count == 0) {
534
r->save_callback (NM_SECRET_AGENT (r->agent), r->connection, NULL, r->callback_data);
540
write_one_secret_to_keyring (NMSetting *setting,
546
Request *r = user_data;
547
GType type = G_VALUE_TYPE (value);
549
const char *setting_name;
550
GnomeKeyringAttributeList *attrs;
551
char *display_name = NULL;
553
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
555
/* Non-secrets obviously don't get saved in the keyring */
556
if (!(flags & NM_SETTING_PARAM_SECRET))
559
/* Don't system-owned or always-ask secrets */
560
if (!nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
562
if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
565
/* VPN secrets are handled by the VPN plugins */
566
if ( (type == DBUS_TYPE_G_MAP_OF_STRING)
567
&& NM_IS_SETTING_VPN (setting)
568
&& !strcmp (key, NM_SETTING_VPN_SECRETS))
571
setting_name = nm_setting_get_name (setting);
572
if (type != G_TYPE_STRING) {
573
g_warning ("Unhandled setting secret type (write) '%s/%s' : '%s'",
574
setting_name, key, g_type_name (type));
578
secret = g_value_get_string (value);
579
if (!secret || !strlen (secret))
582
attrs = utils_create_keyring_add_attr_list (r->connection, NULL, NULL,
587
call = keyring_call_new (r);
588
call->keyring_id = gnome_keyring_item_create (NULL,
589
GNOME_KEYRING_ITEM_GENERIC_SECRET,
598
gnome_keyring_attribute_list_free (attrs);
599
g_free (display_name);
603
save_delete_cb (NMSecretAgent *agent,
604
NMConnection *connection,
608
Request *r = user_data;
610
/* Ignore errors; now save all new secrets */
611
nm_connection_for_each_setting_value (connection, write_one_secret_to_keyring, r);
615
save_secrets (NMSecretAgent *agent,
616
NMConnection *connection,
617
const char *connection_path,
618
NMSecretAgentSaveSecretsFunc callback,
619
gpointer callback_data)
621
AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
624
r = request_new (agent, connection, connection_path, NULL, NULL, FALSE, NULL, callback, NULL, callback_data);
625
g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r);
627
/* First delete any existing items in the keyring */
628
nm_secret_agent_delete_secrets (agent, connection, save_delete_cb, r);
631
/*******************************************************/
634
keyring_delete_cb (GnomeKeyringResult result, gpointer user_data)
640
delete_find_items_cb (GnomeKeyringResult result, GList *list, gpointer user_data)
642
KeyringCall *call = user_data;
643
Request *r = call->r;
645
GError *error = NULL;
647
r->keyring_ids = g_slist_remove (r->keyring_ids, call->keyring_id);
649
if ((result == GNOME_KEYRING_RESULT_OK) || (result == GNOME_KEYRING_RESULT_NO_MATCH)) {
650
for (iter = list; iter != NULL; iter = g_list_next (iter)) {
651
GnomeKeyringFound *found = (GnomeKeyringFound *) iter->data;
653
gnome_keyring_item_delete (found->keyring, found->item_id, keyring_delete_cb, NULL, NULL);
656
error = g_error_new (NM_SECRET_AGENT_ERROR,
657
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
658
"The request could not be completed. Keyring result: %d",
662
r->delete_callback (r->agent, r->connection, error, r->callback_data);
667
delete_secrets (NMSecretAgent *agent,
668
NMConnection *connection,
669
const char *connection_path,
670
NMSecretAgentDeleteSecretsFunc callback,
671
gpointer callback_data)
673
AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent);
675
NMSettingConnection *s_con;
679
r = request_new (agent, connection, connection_path, NULL, NULL, FALSE, NULL, NULL, callback, callback_data);
680
g_hash_table_insert (priv->requests, GUINT_TO_POINTER (r->id), r);
682
s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
684
uuid = nm_setting_connection_get_uuid (s_con);
687
call = keyring_call_new (r);
688
call->keyring_id = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
689
delete_find_items_cb,
693
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
698
/*******************************************************/
701
applet_agent_new (void)
703
return (AppletAgent *) g_object_new (APPLET_TYPE_AGENT,
704
NM_SECRET_AGENT_IDENTIFIER, "org.freedesktop.nm-applet",
709
agent_registration_result_cb (NMSecretAgent *agent, GError *error, gpointer user_data)
712
g_warning ("Failed to register as an agent: (%d) %s", error->code, error->message);
716
applet_agent_init (AppletAgent *self)
718
AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (self);
720
priv->requests = g_hash_table_new (g_direct_hash, g_direct_equal);
722
g_signal_connect (self, NM_SECRET_AGENT_REGISTRATION_RESULT,
723
G_CALLBACK (agent_registration_result_cb), NULL);
727
dispose (GObject *object)
729
AppletAgent *self = APPLET_AGENT (object);
730
AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (self);
732
if (!priv->disposed) {
736
/* Mark any outstanding requests as canceled */
737
g_hash_table_iter_init (&iter, priv->requests);
738
while (g_hash_table_iter_next (&iter, NULL, &data)) {
744
g_hash_table_destroy (priv->requests);
745
priv->disposed = TRUE;
748
G_OBJECT_CLASS (applet_agent_parent_class)->dispose (object);
752
applet_agent_class_init (AppletAgentClass *agent_class)
754
GObjectClass *object_class = G_OBJECT_CLASS (agent_class);
755
NMSecretAgentClass *parent_class = NM_SECRET_AGENT_CLASS (agent_class);
757
g_type_class_add_private (agent_class, sizeof (AppletAgentPrivate));
759
/* virtual methods */
760
object_class->dispose = dispose;
761
parent_class->get_secrets = get_secrets;
762
parent_class->cancel_get_secrets = cancel_get_secrets;
763
parent_class->save_secrets = save_secrets;
764
parent_class->delete_secrets = delete_secrets;
767
signals[GET_SECRETS] =
768
g_signal_new (APPLET_AGENT_GET_SECRETS,
769
G_OBJECT_CLASS_TYPE (object_class),
771
G_STRUCT_OFFSET (AppletAgentClass, get_secrets),
773
nma_marshal_VOID__POINTER_POINTER_STRING_POINTER_UINT_POINTER_POINTER,
775
G_TYPE_POINTER, G_TYPE_POINTER, G_TYPE_STRING, G_TYPE_POINTER, G_TYPE_UINT, G_TYPE_POINTER, G_TYPE_POINTER);
777
signals[CANCEL_SECRETS] =
778
g_signal_new (APPLET_AGENT_CANCEL_SECRETS,
779
G_OBJECT_CLASS_TYPE (object_class),
781
G_STRUCT_OFFSET (AppletAgentClass, cancel_secrets),
783
g_cclosure_marshal_VOID__POINTER,
784
G_TYPE_NONE, 1, G_TYPE_POINTER);