Line data Source code
1 : /*
2 : * gnome-keyring
3 : *
4 : * Copyright (C) 2008 Stefan Walter
5 : *
6 : * This program is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU Lesser General Public License as
8 : * published by the Free Software Foundation; either version 2.1 of
9 : * the License, or (at your option) any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this program; if not, see
18 : * <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include "config.h"
22 :
23 : #include "gkd-secret-change.h"
24 : #include "gkd-secret-prompt.h"
25 : #include "gkd-secret-secret.h"
26 : #include "gkd-secret-service.h"
27 : #include "gkd-secret-session.h"
28 : #include "gkd-secret-types.h"
29 :
30 : #include "egg/egg-error.h"
31 : #include "egg/egg-secure-memory.h"
32 :
33 : #include "pkcs11/pkcs11i.h"
34 :
35 : #include <glib/gi18n.h>
36 :
37 : #include <gck/gck.h>
38 : #include <gcr/gcr-base.h>
39 :
40 : #include <string.h>
41 :
42 : enum {
43 : PROP_0,
44 : PROP_COLLECTION_PATH
45 : };
46 :
47 : struct _GkdSecretChange {
48 : GkdSecretPrompt parent;
49 : gchar *collection_path;
50 : GckSession *session;
51 : GkdSecretSecret *master;
52 : GckObject *ocred;
53 : gboolean unlocked;
54 : gboolean confirmed;
55 : };
56 :
57 : struct _GkdSecretChangeClass {
58 : GkdSecretPromptClass parent_class;
59 : };
60 :
61 : static void perform_prompting (GkdSecretChange *self,
62 : GckObject *collection);
63 :
64 0 : G_DEFINE_TYPE (GkdSecretChange, gkd_secret_change, GKD_SECRET_TYPE_PROMPT);
65 :
66 : static void
67 0 : setup_original_prompt (GkdSecretChange *self,
68 : GckObject *collection)
69 : {
70 0 : GcrPrompt *prompt = GCR_PROMPT (self);
71 0 : GError *error = NULL;
72 : gpointer data;
73 : gsize n_data;
74 : gchar *label;
75 : gchar *text;
76 :
77 0 : data = gck_object_get_data (collection, CKA_LABEL, NULL, &n_data, &error);
78 0 : if (!data) {
79 0 : g_warning ("couldn't get label for collection: %s", egg_error_message (error));
80 0 : g_clear_error (&error);
81 : }
82 :
83 0 : if (!data || !n_data)
84 0 : label = g_strdup (_("Unnamed"));
85 : else
86 0 : label = g_strndup (data, n_data);
87 0 : g_free (data);
88 :
89 0 : text = g_strdup_printf (_("Enter the old password for the “%s” keyring"), label);
90 0 : gcr_prompt_set_message (prompt, text);
91 0 : g_free (text);
92 :
93 0 : text = g_strdup_printf (_("An application wants to change the password for the “%s” keyring. "
94 : "Enter the old password for it."), label);
95 0 : gcr_prompt_set_description (prompt, text);
96 0 : g_free (text);
97 :
98 0 : gcr_prompt_set_password_new (prompt, FALSE);
99 0 : gcr_prompt_set_continue_label (prompt, _("Continue"));
100 0 : }
101 :
102 : static void
103 0 : setup_password_prompt (GkdSecretChange *self,
104 : GckObject *collection)
105 : {
106 0 : GcrPrompt *prompt = GCR_PROMPT (self);
107 0 : GError *error = NULL;
108 : gpointer data;
109 : gsize n_data;
110 : gchar *label;
111 : gchar *text;
112 :
113 0 : data = gck_object_get_data (collection, CKA_LABEL, NULL, &n_data, &error);
114 0 : if (!data) {
115 0 : g_warning ("couldn't get label for collection: %s", egg_error_message (error));
116 0 : g_clear_error (&error);
117 : }
118 :
119 0 : if (!data || !n_data)
120 0 : label = g_strdup (_("Unnamed"));
121 : else
122 0 : label = g_strndup (data, n_data);
123 0 : g_free (data);
124 :
125 0 : text = g_strdup_printf (_("Choose a new password for the “%s” keyring"), label);
126 0 : gcr_prompt_set_message (prompt, text);
127 0 : g_free (text);
128 :
129 0 : text = g_strdup_printf (_("An application wants to change the password for the “%s” keyring. "
130 : "Choose the new password you want to use for it."), label);
131 0 : gcr_prompt_set_description (prompt, text);
132 0 : g_free (text);
133 :
134 0 : gcr_prompt_set_password_new (prompt, TRUE);
135 0 : gcr_prompt_set_continue_label (prompt, _("Continue"));
136 0 : gcr_prompt_set_warning (prompt, NULL);
137 0 : }
138 :
139 : static void
140 0 : setup_confirmation_prompt (GkdSecretChange *self)
141 : {
142 0 : gcr_prompt_set_message (GCR_PROMPT (self), _("Store passwords unencrypted?"));
143 0 : gcr_prompt_set_description (GCR_PROMPT (self),
144 0 : _("By choosing to use a blank password, your stored passwords will not be safely encrypted. "
145 : "They will be accessible by anyone with access to your files."));
146 0 : gcr_prompt_set_continue_label (GCR_PROMPT (self), _("Continue"));
147 0 : }
148 :
149 : static void
150 0 : set_warning_wrong (GkdSecretChange *self)
151 : {
152 0 : gcr_prompt_set_warning (GCR_PROMPT (self), _("The original password was incorrect"));
153 0 : }
154 :
155 : static void
156 0 : on_prompt_original_complete (GObject *source,
157 : GAsyncResult *result,
158 : gpointer user_data)
159 : {
160 0 : GkdSecretChange *self = GKD_SECRET_CHANGE (source);
161 0 : GkdSecretPrompt *prompt = GKD_SECRET_PROMPT (source);
162 0 : GckBuilder builder = GCK_BUILDER_INIT;
163 0 : gboolean continue_prompting = TRUE;
164 : GkdSecretSecret *original;
165 : GckAttributes *attrs;
166 0 : GError *error = NULL;
167 : GckObject *collection;
168 :
169 0 : gcr_prompt_password_finish (GCR_PROMPT (source), result, &error);
170 0 : if (error != NULL) {
171 0 : gkd_secret_prompt_dismiss_with_error (prompt, error);
172 0 : g_error_free (error);
173 0 : return;
174 : }
175 :
176 : /* The prompt was cancelled */
177 0 : original = gkd_secret_prompt_take_secret (prompt);
178 0 : if (original == NULL) {
179 0 : gkd_secret_prompt_dismiss (prompt);
180 0 : return;
181 : }
182 :
183 0 : collection = gkd_secret_prompt_lookup_collection (prompt, self->collection_path);
184 0 : if (collection != NULL) {
185 0 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_CREDENTIAL);
186 0 : gck_builder_add_boolean (&builder, CKA_TOKEN, FALSE);
187 0 : gck_builder_add_ulong (&builder, CKA_G_OBJECT, gck_object_get_handle (collection));
188 :
189 0 : attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
190 :
191 : /* Create the original credential, in order to make sure we can unlock the collection */
192 0 : self->ocred = gkd_secret_session_create_credential (original->session,
193 : self->session, attrs,
194 : original, &error);
195 :
196 0 : gck_attributes_unref (attrs);
197 :
198 : /* The unlock failed because password was bad */
199 0 : if (g_error_matches (error, GCK_ERROR, CKR_PIN_INCORRECT)) {
200 0 : set_warning_wrong (self);
201 0 : g_error_free (error);
202 :
203 : /* The unlock failed for some other reason */
204 0 : } else if (error != NULL) {
205 0 : continue_prompting = FALSE;
206 0 : gkd_secret_prompt_dismiss_with_error (prompt, error);
207 0 : g_error_free (error);
208 :
209 : /* The unlock succeeded */
210 : } else {
211 0 : if (self->session == NULL)
212 0 : self->session = gck_object_get_session (self->ocred);
213 0 : self->unlocked = TRUE;
214 : }
215 : }
216 :
217 0 : if (continue_prompting)
218 0 : perform_prompting (self, collection);
219 :
220 0 : gkd_secret_secret_free (original);
221 0 : g_clear_object (&collection);
222 : }
223 :
224 : static void
225 0 : on_prompt_password_complete (GObject *source,
226 : GAsyncResult *result,
227 : gpointer user_data)
228 : {
229 0 : GkdSecretChange *self = GKD_SECRET_CHANGE (source);
230 0 : GkdSecretPrompt *prompt = GKD_SECRET_PROMPT (source);
231 0 : GError *error = NULL;
232 : GckObject *collection;
233 :
234 0 : gcr_prompt_password_finish (GCR_PROMPT (source), result, &error);
235 0 : if (error != NULL) {
236 0 : gkd_secret_prompt_dismiss_with_error (prompt, error);
237 0 : g_error_free (error);
238 0 : return;
239 : }
240 :
241 0 : self->master = gkd_secret_prompt_take_secret (prompt);
242 0 : if (self->master == NULL) {
243 0 : gkd_secret_prompt_dismiss (prompt);
244 0 : return;
245 : }
246 :
247 : /* If the password strength is greater than zero, then don't confirm */
248 0 : if (gcr_prompt_get_password_strength (GCR_PROMPT (source)) > 0)
249 0 : self->confirmed = TRUE;
250 :
251 0 : collection = gkd_secret_prompt_lookup_collection (prompt, self->collection_path);
252 0 : perform_prompting (self, collection);
253 0 : g_clear_object (&collection);
254 : }
255 :
256 : static void
257 0 : on_prompt_confirmation_complete (GObject *source,
258 : GAsyncResult *result,
259 : gpointer user_data)
260 : {
261 0 : GkdSecretChange *self = GKD_SECRET_CHANGE (source);
262 0 : GkdSecretPrompt *prompt = GKD_SECRET_PROMPT (source);
263 0 : GError *error = NULL;
264 : GckObject *collection;
265 :
266 0 : self->confirmed = gcr_prompt_confirm_finish (GCR_PROMPT (source), result, &error);
267 0 : if (error != NULL) {
268 0 : gkd_secret_prompt_dismiss_with_error (prompt, error);
269 0 : g_error_free (error);
270 0 : return;
271 : }
272 :
273 : /* If not confirmed, then prompt again */
274 0 : if (!self->confirmed) {
275 0 : gkd_secret_secret_free (self->master);
276 0 : self->master = NULL;
277 : }
278 :
279 0 : collection = gkd_secret_prompt_lookup_collection (prompt, self->collection_path);
280 0 : perform_prompting (self, collection);
281 0 : g_clear_object (&collection);
282 : }
283 :
284 : static void
285 0 : perform_prompting (GkdSecretChange *self,
286 : GckObject *collection)
287 : {
288 0 : GkdSecretPrompt *prompt = GKD_SECRET_PROMPT (self);
289 0 : GError *error = NULL;
290 :
291 : /* Collection doesn't exist, just go away */
292 0 : if (collection == NULL) {
293 0 : gkd_secret_prompt_dismiss (prompt);
294 :
295 : /* Get the original password and unlock */
296 0 : } else if (!self->unlocked) {
297 0 : setup_original_prompt (self, collection);
298 0 : gcr_prompt_password_async (GCR_PROMPT (self),
299 : gkd_secret_prompt_get_cancellable (prompt),
300 : on_prompt_original_complete, NULL);
301 :
302 : /* Get the new password */
303 0 : } else if (self->master == NULL) {
304 0 : setup_password_prompt (self, collection);
305 0 : gcr_prompt_password_async (GCR_PROMPT (self),
306 : gkd_secret_prompt_get_cancellable (prompt),
307 : on_prompt_password_complete, NULL);
308 :
309 : /* Check that the password is not empty */
310 0 : } else if (!self->confirmed) {
311 0 : setup_confirmation_prompt (self);
312 0 : gcr_prompt_confirm_async (GCR_PROMPT (self),
313 : gkd_secret_prompt_get_cancellable (prompt),
314 : on_prompt_confirmation_complete, NULL);
315 :
316 : /* Actually create the keyring */
317 0 : } else if (gkd_secret_change_with_secrets (collection, self->session,
318 : NULL, self->master, &error)) {
319 0 : gkd_secret_prompt_complete (prompt);
320 :
321 : /* Failed */
322 : } else {
323 0 : gkd_secret_prompt_dismiss_with_error (prompt, error);
324 0 : g_error_free (error);
325 : }
326 0 : }
327 :
328 : static void
329 0 : gkd_secret_change_prompt_ready (GkdSecretPrompt *prompt)
330 : {
331 0 : GkdSecretChange *self = GKD_SECRET_CHANGE (prompt);
332 : GckObject *collection;
333 :
334 0 : collection = gkd_secret_prompt_lookup_collection (prompt, self->collection_path);
335 0 : perform_prompting (self, collection);
336 0 : g_clear_object (&collection);
337 0 : }
338 :
339 : static GVariant *
340 0 : gkd_secret_change_encode_result (GkdSecretPrompt *base)
341 : {
342 0 : return g_variant_new_variant (g_variant_new_string (""));
343 : }
344 :
345 : static void
346 0 : gkd_secret_change_init (GkdSecretChange *self)
347 : {
348 0 : gcr_prompt_set_title (GCR_PROMPT (self), _("Change Keyring Password"));
349 0 : }
350 :
351 : static void
352 0 : gkd_secret_change_dispose (GObject *obj)
353 : {
354 0 : GkdSecretChange *self = GKD_SECRET_CHANGE (obj);
355 :
356 0 : if (self->ocred) {
357 0 : gck_object_destroy (self->ocred, NULL, NULL);
358 0 : g_object_unref (self->ocred);
359 0 : self->ocred = NULL;
360 : }
361 :
362 0 : G_OBJECT_CLASS (gkd_secret_change_parent_class)->dispose (obj);
363 0 : }
364 :
365 : static void
366 0 : gkd_secret_change_finalize (GObject *obj)
367 : {
368 0 : GkdSecretChange *self = GKD_SECRET_CHANGE (obj);
369 :
370 0 : g_free (self->collection_path);
371 0 : if (self->master)
372 0 : gkd_secret_secret_free (self->master);
373 0 : if (self->session)
374 0 : g_object_unref (self->session);
375 :
376 0 : G_OBJECT_CLASS (gkd_secret_change_parent_class)->finalize (obj);
377 0 : }
378 :
379 : static void
380 0 : gkd_secret_change_set_property (GObject *obj, guint prop_id, const GValue *value,
381 : GParamSpec *pspec)
382 : {
383 0 : GkdSecretChange *self = GKD_SECRET_CHANGE (obj);
384 :
385 0 : switch (prop_id) {
386 0 : case PROP_COLLECTION_PATH:
387 0 : g_return_if_fail (!self->collection_path);
388 0 : self->collection_path = g_value_dup_string (value);
389 0 : g_return_if_fail (self->collection_path);
390 0 : break;
391 0 : default:
392 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
393 0 : break;
394 : }
395 : }
396 :
397 : static void
398 0 : gkd_secret_change_get_property (GObject *obj, guint prop_id, GValue *value,
399 : GParamSpec *pspec)
400 : {
401 0 : GkdSecretChange *self = GKD_SECRET_CHANGE (obj);
402 :
403 0 : switch (prop_id) {
404 0 : case PROP_COLLECTION_PATH:
405 0 : g_value_set_string (value, self->collection_path);
406 0 : break;
407 0 : default:
408 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
409 0 : break;
410 : }
411 0 : }
412 :
413 : static void
414 0 : gkd_secret_change_class_init (GkdSecretChangeClass *klass)
415 : {
416 0 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
417 0 : GkdSecretPromptClass *prompt_class = GKD_SECRET_PROMPT_CLASS (klass);
418 :
419 0 : gobject_class->dispose = gkd_secret_change_dispose;
420 0 : gobject_class->finalize = gkd_secret_change_finalize;
421 0 : gobject_class->get_property = gkd_secret_change_get_property;
422 0 : gobject_class->set_property = gkd_secret_change_set_property;
423 :
424 0 : prompt_class->prompt_ready = gkd_secret_change_prompt_ready;
425 0 : prompt_class->encode_result = gkd_secret_change_encode_result;
426 :
427 0 : g_object_class_install_property (gobject_class, PROP_COLLECTION_PATH,
428 : g_param_spec_string ("collection-path", "Collection Path", "Collection Path",
429 : "/", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
430 0 : }
431 :
432 : /* -----------------------------------------------------------------------------
433 : * PUBLIC
434 : */
435 :
436 : GkdSecretChange*
437 0 : gkd_secret_change_new (GkdSecretService *service, const gchar *caller,
438 : const gchar *path)
439 : {
440 : const gchar *prompter_name;
441 :
442 0 : g_return_val_if_fail (GKD_SECRET_IS_SERVICE (service), NULL);
443 0 : g_return_val_if_fail (caller, NULL);
444 0 : g_return_val_if_fail (path, NULL);
445 :
446 0 : prompter_name = g_getenv ("GNOME_KEYRING_TEST_PROMPTER");
447 0 : return g_object_new (GKD_SECRET_TYPE_CHANGE,
448 : "service", service,
449 : "caller", caller,
450 : "collection-path", path,
451 : "bus-name", prompter_name,
452 : NULL);
453 : }
454 :
455 : gboolean
456 0 : gkd_secret_change_with_secrets (GckObject *collection,
457 : GckSession *session,
458 : GkdSecretSecret *original,
459 : GkdSecretSecret *master,
460 : GError **error)
461 : {
462 0 : GckBuilder builder = GCK_BUILDER_INIT;
463 0 : GckAttributes *attrs = NULL;
464 0 : gboolean result = FALSE;
465 0 : GckObject *ocred = NULL;
466 0 : GckObject *mcred = NULL;
467 :
468 0 : g_assert (GCK_IS_OBJECT (collection));
469 0 : g_assert (session == NULL || GCK_IS_SESSION (session));
470 0 : g_assert (master != NULL);
471 0 : g_assert (error == NULL || *error == NULL);
472 :
473 : /* Create the new credential */
474 0 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_CREDENTIAL);
475 0 : gck_builder_add_boolean (&builder, CKA_TOKEN, FALSE);
476 0 : attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
477 0 : mcred = gkd_secret_session_create_credential (master->session, session, attrs, master, error);
478 0 : gck_builder_add_all (&builder, attrs);
479 0 : gck_attributes_unref (attrs);
480 :
481 0 : if (mcred == NULL)
482 0 : goto cleanup;
483 :
484 : /* Create the original credential, in order to make sure we can the collection */
485 0 : if (original) {
486 0 : gck_builder_add_ulong (&builder, CKA_G_OBJECT, gck_object_get_handle (collection));
487 0 : attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
488 0 : ocred = gkd_secret_session_create_credential (original->session, session, attrs, original, error);
489 0 : gck_attributes_unref (attrs);
490 :
491 0 : if (ocred == NULL)
492 0 : goto cleanup;
493 : }
494 :
495 0 : gck_builder_clear (&builder);
496 0 : gck_builder_add_ulong (&builder, CKA_G_CREDENTIAL, gck_object_get_handle (mcred));
497 :
498 : /* Now set the collection credentials to the first one */
499 0 : result = gck_object_set (collection, gck_builder_end (&builder), NULL, error);
500 :
501 0 : cleanup:
502 0 : if (ocred) {
503 : /* Always destroy the original credential */
504 0 : gck_object_destroy (ocred, NULL, NULL);
505 0 : g_object_unref (ocred);
506 : }
507 0 : if (mcred) {
508 : /* Always destroy the master credential */
509 0 : gck_object_destroy (mcred, NULL, NULL);
510 0 : g_object_unref (mcred);
511 : }
512 :
513 0 : gck_builder_clear (&builder);
514 0 : return result;
515 : }
|