1
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
3
* Copyright 2011 Red Hat, Inc.
4
* 2011 Giovanni Campagna <scampa.giovanni@gmail.com>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License along
17
* with this program; if not, write to the Free Software Foundation, Inc.,
18
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24
#include <gnome-keyring.h>
26
#include "shell-network-agent.h"
27
#include "shell-marshal.h"
31
SIGNAL_CANCEL_REQUEST,
35
static gint signals[SIGNAL_LAST];
39
ShellNetworkAgent *self;
42
NMConnection *connection;
45
NMSecretAgentGetSecretsFlags flags;
46
NMSecretAgentGetSecretsFunc callback;
47
gpointer callback_data;
49
/* <gchar *setting_key, gchar *secret> */
53
struct _ShellNetworkAgentPrivate {
54
/* <gchar *request_id, ShellAgentRequest *request> */
58
G_DEFINE_TYPE (ShellNetworkAgent, shell_network_agent, NM_TYPE_SECRET_AGENT)
61
shell_agent_request_free (gpointer data)
63
ShellAgentRequest *request = data;
65
if (request->keyring_op)
66
gnome_keyring_cancel_request (request->keyring_op);
68
g_object_unref (request->self);
69
g_object_unref (request->connection);
70
g_free (request->setting_name);
71
g_strfreev (request->hints);
73
g_hash_table_destroy (request->entries);
75
g_slice_free (ShellAgentRequest, request);
79
shell_network_agent_init (ShellNetworkAgent *agent)
81
ShellNetworkAgentPrivate *priv;
83
priv = agent->priv = G_TYPE_INSTANCE_GET_PRIVATE (agent, SHELL_TYPE_NETWORK_AGENT, ShellNetworkAgentPrivate);
85
priv->requests = g_hash_table_new_full (g_str_hash, g_str_equal,
86
g_free, shell_agent_request_free);
90
shell_network_agent_finalize (GObject *object)
92
ShellNetworkAgentPrivate *priv = SHELL_NETWORK_AGENT (object)->priv;
98
error = g_error_new (NM_SECRET_AGENT_ERROR,
99
NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
100
"The secret agent is going away");
102
g_hash_table_iter_init (&iter, priv->requests);
103
while (g_hash_table_iter_next (&iter, &key, &value))
105
ShellAgentRequest *request = value;
107
request->callback (NM_SECRET_AGENT (object),
110
request->callback_data);
113
g_hash_table_destroy (priv->requests);
114
g_error_free (error);
116
G_OBJECT_CLASS (shell_network_agent_parent_class)->finalize (object);
120
request_secrets_from_ui (ShellAgentRequest *request)
122
g_signal_emit (request->self, signals[SIGNAL_NEW_REQUEST], 0,
125
request->setting_name,
130
check_always_ask_cb (NMSetting *setting,
136
gboolean *always_ask = user_data;
137
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
139
if (flags & NM_SETTING_PARAM_SECRET)
141
if (nm_setting_get_secret_flags (setting, key, &secret_flags, NULL))
143
if (secret_flags & NM_SETTING_SECRET_FLAG_NOT_SAVED)
150
has_always_ask (NMSetting *setting)
152
gboolean always_ask = FALSE;
154
nm_setting_enumerate_values (setting, check_always_ask_cb, &always_ask);
159
is_connection_always_ask (NMConnection *connection)
161
NMSettingConnection *s_con;
165
/* For the given connection type, check if the secrets for that connection
166
* are always-ask or not.
168
s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
170
ctype = nm_setting_connection_get_connection_type (s_con);
172
setting = nm_connection_get_setting_by_name (connection, ctype);
173
g_return_val_if_fail (setting != NULL, FALSE);
175
if (has_always_ask (setting))
178
/* Try type-specific settings too; be a bit paranoid and only consider
179
* secrets from settings relevant to the connection type.
181
if (NM_IS_SETTING_WIRELESS (setting))
183
setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS_SECURITY);
184
if (setting && has_always_ask (setting))
186
setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
187
if (setting && has_always_ask (setting))
190
else if (NM_IS_SETTING_WIRED (setting))
192
setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
193
if (setting && has_always_ask (setting))
195
setting = nm_connection_get_setting (connection, NM_TYPE_SETTING_802_1X);
196
if (setting && has_always_ask (setting))
204
gvalue_destroy_notify (gpointer data)
206
GValue *value = data;
207
g_value_unset (value);
208
g_slice_free (GValue, value);
212
strv_has (gchar **haystack,
216
for (iter = *haystack; iter; iter++)
218
if (g_strcmp0 (iter, needle) == 0)
226
get_secrets_keyring_cb (GnomeKeyringResult result,
230
ShellAgentRequest *closure = user_data;
231
ShellNetworkAgent *self = closure->self;
232
ShellNetworkAgentPrivate *priv = self->priv;
233
GError *error = NULL;
238
closure->keyring_op = NULL;
240
if (result == GNOME_KEYRING_RESULT_CANCELLED)
243
NM_SECRET_AGENT_ERROR,
244
NM_SECRET_AGENT_ERROR_USER_CANCELED,
245
"The secret request was cancelled by the user");
247
closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
252
if (result != GNOME_KEYRING_RESULT_OK &&
253
result != GNOME_KEYRING_RESULT_NO_MATCH)
256
NM_SECRET_AGENT_ERROR,
257
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
258
"Internal error while retrieving secrets from the keyring (result %d)", result);
260
closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, NULL, error, closure->callback_data);
265
for (iter = list; iter; iter = g_list_next (iter))
267
GnomeKeyringFound *item = iter->data;
270
for (i = 0; i < item->attributes->len; i++)
272
GnomeKeyringAttribute *attr = &gnome_keyring_attribute_list_index (item->attributes, i);
274
if (g_strcmp0 (attr->name, SHELL_KEYRING_SK_TAG) == 0
275
&& (attr->type == GNOME_KEYRING_ATTRIBUTE_TYPE_STRING))
277
gchar *secret_name = g_strdup (attr->value.string);
278
GValue *secret_value = g_slice_new0 (GValue);
279
g_value_init (secret_value, G_TYPE_STRING);
280
g_value_set_string (secret_value, item->secret);
282
g_hash_table_insert (closure->entries, secret_name, secret_value);
285
n_found += strv_has (closure->hints, secret_name);
295
(closure->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION))
297
/* Even if n_found == 0, secrets is not necessarily empty */
298
nm_connection_update_secrets (closure->connection, closure->setting_name, closure->entries, NULL);
300
request_secrets_from_ui (closure);
304
outer = g_hash_table_new (g_str_hash, g_str_equal);
305
g_hash_table_insert (outer, closure->setting_name, closure->entries);
307
closure->callback (NM_SECRET_AGENT (closure->self), closure->connection, outer, NULL, closure->callback_data);
309
g_hash_table_destroy (outer);
312
g_hash_table_remove (priv->requests, closure->request_id);
313
g_clear_error (&error);
317
shell_network_agent_get_secrets (NMSecretAgent *agent,
318
NMConnection *connection,
319
const gchar *connection_path,
320
const gchar *setting_name,
322
NMSecretAgentGetSecretsFlags flags,
323
NMSecretAgentGetSecretsFunc callback,
324
gpointer callback_data)
326
ShellNetworkAgent *self = SHELL_NETWORK_AGENT (agent);
327
ShellAgentRequest *request = g_slice_new (ShellAgentRequest);
329
request->self = g_object_ref (self);
330
request->connection = g_object_ref (connection);
331
request->setting_name = g_strdup (setting_name);
332
request->hints = g_strdupv ((gchar **)hints);
333
request->flags = flags;
334
request->callback = callback;
335
request->callback_data = callback_data;
336
request->entries = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, gvalue_destroy_notify);
338
request->request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
339
g_hash_table_replace (self->priv->requests, request->request_id, request);
341
if ((flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) ||
342
(flags && NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION
343
&& is_connection_always_ask (request->connection)))
345
request_secrets_from_ui (request);
349
request->keyring_op = gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
350
get_secrets_keyring_cb,
352
NULL, /* GDestroyNotify */
353
SHELL_KEYRING_UUID_TAG,
354
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
355
nm_connection_get_uuid (connection),
356
SHELL_KEYRING_SN_TAG,
357
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
363
shell_network_agent_set_password (ShellNetworkAgent *self,
366
gchar *setting_value)
368
ShellNetworkAgentPrivate *priv;
369
ShellAgentRequest *request;
372
g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
375
request = g_hash_table_lookup (priv->requests, request_id);
377
value = g_slice_new0 (GValue);
378
g_value_init (value, G_TYPE_STRING);
379
g_value_set_string (value, setting_value);
381
g_hash_table_replace (request->entries, g_strdup (setting_key), value);
385
shell_network_agent_respond (ShellNetworkAgent *self,
389
ShellNetworkAgentPrivate *priv;
390
ShellAgentRequest *request;
394
g_return_if_fail (SHELL_IS_NETWORK_AGENT (self));
397
request = g_hash_table_lookup (priv->requests, request_id);
401
GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
402
NM_SECRET_AGENT_ERROR_USER_CANCELED,
403
"Network dialog was canceled by the user");
405
request->callback (NM_SECRET_AGENT (self), request->connection, NULL, error, request->callback_data);
406
g_error_free (error);
407
g_hash_table_remove (priv->requests, request_id);
411
/* Save updated secrets */
412
dup = nm_connection_duplicate (request->connection);
413
nm_connection_update_secrets (dup, request->setting_name, request->entries, NULL);
415
nm_secret_agent_save_secrets (NM_SECRET_AGENT (self), dup, NULL, NULL);
417
outer = g_hash_table_new (g_str_hash, g_str_equal);
418
g_hash_table_insert (outer, request->setting_name, request->entries);
420
request->callback (NM_SECRET_AGENT (self), request->connection, outer, NULL, request->callback_data);
422
g_hash_table_destroy (outer);
423
g_object_unref (dup);
424
g_hash_table_remove (priv->requests, request_id);
428
shell_network_agent_cancel_get_secrets (NMSecretAgent *agent,
429
const gchar *connection_path,
430
const gchar *setting_name)
432
ShellNetworkAgent *self = SHELL_NETWORK_AGENT (agent);
433
ShellNetworkAgentPrivate *priv = self->priv;
435
gchar *request_id = g_strdup_printf ("%s/%s", connection_path, setting_name);
436
ShellAgentRequest *request = g_hash_table_lookup (priv->requests, request_id);
438
GError *error = g_error_new (NM_SECRET_AGENT_ERROR,
439
NM_SECRET_AGENT_ERROR_AGENT_CANCELED,
440
"Canceled by NetworkManager");
441
request->callback (agent, request->connection, NULL, error, request->callback_data);
443
g_signal_emit (self, signals[SIGNAL_CANCEL_REQUEST], 0, request_id);
445
g_hash_table_remove (priv->requests, request_id);
447
g_error_free (error);
450
/************************* saving of secrets ****************************************/
452
static GnomeKeyringAttributeList *
453
create_keyring_add_attr_list (NMConnection *connection,
454
const gchar *connection_uuid,
455
const gchar *connection_id,
456
const gchar *setting_name,
457
const gchar *setting_key,
458
gchar **out_display_name)
460
GnomeKeyringAttributeList *attrs = NULL;
461
NMSettingConnection *s_con;
465
s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
466
g_return_val_if_fail (s_con != NULL, NULL);
467
connection_uuid = nm_setting_connection_get_uuid (s_con);
468
connection_id = nm_setting_connection_get_id (s_con);
471
g_return_val_if_fail (connection_uuid != NULL, NULL);
472
g_return_val_if_fail (connection_id != NULL, NULL);
473
g_return_val_if_fail (setting_name != NULL, NULL);
474
g_return_val_if_fail (setting_key != NULL, NULL);
476
if (out_display_name)
478
*out_display_name = g_strdup_printf ("Network secret for %s/%s/%s",
484
attrs = gnome_keyring_attribute_list_new ();
485
gnome_keyring_attribute_list_append_string (attrs,
486
SHELL_KEYRING_UUID_TAG,
488
gnome_keyring_attribute_list_append_string (attrs,
489
SHELL_KEYRING_SN_TAG,
491
gnome_keyring_attribute_list_append_string (attrs,
492
SHELL_KEYRING_SK_TAG,
499
/* Sort of ref count, indicates the number of secrets we still need to save */
503
NMConnection *connection;
505
gpointer callback_data;
509
keyring_request_free (KeyringRequest *r)
511
g_object_unref (r->self);
512
g_object_unref (r->connection);
514
g_slice_free (KeyringRequest, r);
518
save_secret_cb (GnomeKeyringResult result,
522
KeyringRequest *call = user_data;
523
NMSecretAgentSaveSecretsFunc callback = call->callback;
527
if (call->n_secrets == 0)
530
callback (call->self, call->connection, NULL, call->callback_data);
531
keyring_request_free (call);
536
save_one_secret (KeyringRequest *r,
540
const gchar *display_name)
542
GnomeKeyringAttributeList *attrs;
543
gchar *alt_display_name = NULL;
544
const gchar *setting_name;
545
NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE;
547
/* Only save agent-owned secrets (not system-owned or always-ask) */
548
nm_setting_get_secret_flags (setting, key, &secret_flags, NULL);
549
if (secret_flags != NM_SETTING_SECRET_FLAG_AGENT_OWNED)
552
setting_name = nm_setting_get_name (setting);
553
g_assert (setting_name);
555
attrs = create_keyring_add_attr_list (r->connection, NULL, NULL,
558
display_name ? NULL : &alt_display_name);
561
gnome_keyring_item_create (NULL,
562
GNOME_KEYRING_ITEM_GENERIC_SECRET,
563
display_name ? display_name : alt_display_name,
571
gnome_keyring_attribute_list_free (attrs);
572
g_free (alt_display_name);
576
vpn_secret_iter_cb (const gchar *key,
580
KeyringRequest *r = user_data;
582
const gchar *service_name, *id;
585
if (secret && strlen (secret))
587
setting = nm_connection_get_setting (r->connection, NM_TYPE_SETTING_VPN);
589
service_name = nm_setting_vpn_get_service_type (NM_SETTING_VPN (setting));
590
g_assert (service_name);
591
id = nm_connection_get_id (r->connection);
594
display_name = g_strdup_printf ("VPN %s secret for %s/%s/" NM_SETTING_VPN_SETTING_NAME,
598
save_one_secret (r, setting, key, secret, display_name);
599
g_free (display_name);
604
write_one_secret_to_keyring (NMSetting *setting,
610
KeyringRequest *r = user_data;
613
/* Non-secrets obviously don't get saved in the keyring */
614
if (!(flags & NM_SETTING_PARAM_SECRET))
617
if (NM_IS_SETTING_VPN (setting) && (g_strcmp0 (key, NM_SETTING_VPN_SECRETS) == 0))
619
/* Process VPN secrets specially since it's a hash of secrets, not just one */
620
nm_setting_vpn_foreach_secret (NM_SETTING_VPN (setting),
626
secret = g_value_get_string (value);
627
if (secret && strlen (secret))
628
save_one_secret (r, setting, key, secret, NULL);
633
save_delete_cb (NMSecretAgent *agent,
634
NMConnection *connection,
638
KeyringRequest *r = user_data;
640
/* Ignore errors; now save all new secrets */
641
nm_connection_for_each_setting_value (connection, write_one_secret_to_keyring, r);
643
/* If no secrets actually got saved there may be nothing to do so
644
* try to complete the request here. If there were secrets to save the
645
* request will get completed when those keyring calls return (at the next
646
* mainloop iteration).
648
if (r->n_secrets == 0)
651
((NMSecretAgentSaveSecretsFunc)r->callback) (agent, connection, NULL, r->callback_data);
652
keyring_request_free (r);
657
shell_network_agent_save_secrets (NMSecretAgent *agent,
658
NMConnection *connection,
659
const gchar *connection_path,
660
NMSecretAgentSaveSecretsFunc callback,
661
gpointer callback_data)
665
r = g_slice_new (KeyringRequest);
667
r->self = g_object_ref (agent);
668
r->connection = g_object_ref (connection);
669
r->callback = callback;
670
r->callback_data = callback_data;
672
/* First delete any existing items in the keyring */
673
nm_secret_agent_delete_secrets (agent, connection, save_delete_cb, r);
677
keyring_delete_cb (GnomeKeyringResult result, gpointer user_data)
683
delete_find_items_cb (GnomeKeyringResult result, GList *list, gpointer user_data)
685
KeyringRequest *r = user_data;
687
GError *error = NULL;
688
NMSecretAgentDeleteSecretsFunc callback = r->callback;
690
if ((result == GNOME_KEYRING_RESULT_OK) || (result == GNOME_KEYRING_RESULT_NO_MATCH))
692
for (iter = list; iter != NULL; iter = g_list_next (iter))
694
GnomeKeyringFound *found = (GnomeKeyringFound *) iter->data;
696
gnome_keyring_item_delete (found->keyring, found->item_id, keyring_delete_cb, NULL, NULL);
701
error = g_error_new (NM_SECRET_AGENT_ERROR,
702
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
703
"The request could not be completed. Keyring result: %d",
707
callback (r->self, r->connection, error, r->callback_data);
708
g_clear_error (&error);
712
shell_network_agent_delete_secrets (NMSecretAgent *agent,
713
NMConnection *connection,
714
const gchar *connection_path,
715
NMSecretAgentDeleteSecretsFunc callback,
716
gpointer callback_data)
719
NMSettingConnection *s_con;
722
r = g_slice_new (KeyringRequest);
723
r->n_secrets = 0; /* ignored by delete secrets calls */
724
r->self = g_object_ref (agent);
725
r->connection = g_object_ref (connection);
726
r->callback = callback;
727
r->callback_data = callback_data;
729
s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
731
uuid = nm_setting_connection_get_uuid (s_con);
734
gnome_keyring_find_itemsv (GNOME_KEYRING_ITEM_GENERIC_SECRET,
735
delete_find_items_cb,
737
(GDestroyNotify)keyring_request_free,
738
SHELL_KEYRING_UUID_TAG,
739
GNOME_KEYRING_ATTRIBUTE_TYPE_STRING,
745
shell_network_agent_class_init (ShellNetworkAgentClass *klass)
747
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
748
NMSecretAgentClass *agent_class = NM_SECRET_AGENT_CLASS (klass);
750
gobject_class->finalize = shell_network_agent_finalize;
752
agent_class->get_secrets = shell_network_agent_get_secrets;
753
agent_class->cancel_get_secrets = shell_network_agent_cancel_get_secrets;
754
agent_class->save_secrets = shell_network_agent_save_secrets;
755
agent_class->delete_secrets = shell_network_agent_delete_secrets;
757
signals[SIGNAL_NEW_REQUEST] = g_signal_new ("new-request",
758
G_TYPE_FROM_CLASS (klass),
760
0, /* class offset */
761
NULL, /* accumulator */
762
NULL, /* accu_data */
763
_shell_marshal_VOID__STRING_OBJECT_STRING_BOXED,
764
G_TYPE_NONE, /* return */
771
signals[SIGNAL_CANCEL_REQUEST] = g_signal_new ("cancel-request",
772
G_TYPE_FROM_CLASS (klass),
774
0, /* class offset */
775
NULL, /* accumulator */
776
NULL, /* accu_data */
777
g_cclosure_marshal_VOID__STRING,
782
g_type_class_add_private (klass, sizeof (ShellNetworkAgentPrivate));