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-dbus.h"
24 : #include "gkd-secret-dispatch.h"
25 : #include "gkd-secret-error.h"
26 : #include "gkd-secret-objects.h"
27 : #include "gkd-secret-secret.h"
28 : #include "gkd-secret-session.h"
29 : #include "gkd-secret-service.h"
30 : #include "gkd-secret-types.h"
31 : #include "gkd-secret-unlock.h"
32 : #include "gkd-secret-util.h"
33 : #include "gkd-secrets-generated.h"
34 :
35 : #include "egg/egg-error.h"
36 : #include "egg/egg-secure-memory.h"
37 :
38 : #include "daemon/login/gkd-login.h"
39 :
40 : #include "pkcs11/pkcs11i.h"
41 :
42 : #include <glib/gi18n.h>
43 :
44 : #include <gck/gck.h>
45 :
46 : #include <string.h>
47 :
48 : /*
49 : * We try to serialize unlock requests, so the user doesn't get prompted
50 : * multiple times for the same thing. There are two queues:
51 : * - self->queued: A queue of object paths per unlock requests.
52 : * - unlock_prompt_queue: A queue of unlock requests ready to prompt.
53 : */
54 :
55 : enum {
56 : PROP_0,
57 : PROP_CALLER,
58 : PROP_OBJECT_PATH,
59 : PROP_SERVICE
60 : };
61 :
62 : struct _GkdSecretUnlock {
63 : GObject parent;
64 : gchar *object_path;
65 : GkdSecretService *service;
66 : GkdExportedPrompt *skeleton;
67 : gchar *caller;
68 : gchar *window_id;
69 : GQueue *queued;
70 : gchar *current;
71 : GArray *results;
72 : gboolean prompted;
73 : gboolean completed;
74 : GCancellable *cancellable;
75 : };
76 :
77 : /* Forward declarations */
78 : static void gkd_secret_dispatch_iface (GkdSecretDispatchIface *iface);
79 : static void perform_next_unlock (GkdSecretUnlock *self);
80 :
81 19 : G_DEFINE_TYPE_WITH_CODE (GkdSecretUnlock, gkd_secret_unlock, G_TYPE_OBJECT,
82 : G_IMPLEMENT_INTERFACE (GKD_SECRET_TYPE_DISPATCH, gkd_secret_dispatch_iface));
83 :
84 : static guint unique_prompt_number = 0;
85 : static GQueue unlock_prompt_queue = G_QUEUE_INIT;
86 :
87 : EGG_SECURE_DECLARE (secret_unlock);
88 :
89 : /* -----------------------------------------------------------------------------
90 : * INTERNAL
91 : */
92 :
93 : static GckObject*
94 2 : lookup_collection (GkdSecretUnlock *self, const gchar *path)
95 : {
96 2 : GkdSecretObjects *objects = gkd_secret_service_get_objects (self->service);
97 2 : return gkd_secret_objects_lookup_collection (objects, self->caller, path);
98 : }
99 :
100 : static void
101 1 : emit_collection_unlocked (GkdSecretUnlock *self,
102 : const gchar *path)
103 : {
104 : GkdSecretObjects *objects;
105 : GckObject *collection;
106 :
107 1 : objects = gkd_secret_service_get_objects (self->service);
108 1 : collection = gkd_secret_objects_lookup_collection (objects, self->caller, path);
109 1 : if (collection != NULL) {
110 1 : gkd_secret_objects_emit_collection_locked (objects, collection);
111 1 : g_object_unref (collection);
112 : }
113 1 : }
114 :
115 : static gboolean
116 17 : check_locked_collection (GckObject *collection, gboolean *locked)
117 : {
118 17 : GError *error = NULL;
119 : gpointer value;
120 : gsize n_value;
121 :
122 17 : value = gck_object_get_data (collection, CKA_G_LOCKED, NULL, &n_value, &error);
123 17 : if (value == NULL) {
124 0 : if (!g_error_matches (error, GCK_ERROR, CKR_OBJECT_HANDLE_INVALID))
125 0 : g_warning ("couldn't check locked status of collection: %s",
126 : egg_error_message (error));
127 0 : return FALSE;
128 : }
129 :
130 17 : *locked = (value && n_value == sizeof (CK_BBOOL) && *(CK_BBOOL*)value);
131 17 : g_free (value);
132 17 : return TRUE;
133 : }
134 :
135 : static void
136 17 : common_unlock_attributes (GckBuilder *builder,
137 : GckObject *collection)
138 : {
139 17 : g_assert (builder != NULL);
140 17 : g_assert (GCK_IS_OBJECT (collection));
141 17 : gck_builder_add_ulong (builder, CKA_CLASS, CKO_G_CREDENTIAL);
142 17 : gck_builder_add_ulong (builder, CKA_G_OBJECT, gck_object_get_handle (collection));
143 17 : }
144 :
145 : static gboolean
146 1 : mark_as_complete (GkdSecretUnlock *self, gboolean dismissed)
147 : {
148 : GkdSecretUnlock *other;
149 : const char *value;
150 : gint i;
151 : GVariantBuilder builder;
152 : GVariant *variant;
153 :
154 1 : if (self->completed)
155 0 : return FALSE;
156 1 : self->completed = TRUE;
157 :
158 1 : g_variant_builder_init (&builder, G_VARIANT_TYPE ("ao"));
159 2 : for (i = 0; i < self->results->len; ++i) {
160 1 : value = g_array_index (self->results, gchar*, i);
161 1 : g_variant_builder_add (&builder, "o", value);
162 : }
163 :
164 : /* Emit signal manually, so that we can set the caller as destination */
165 1 : variant = g_variant_new_variant (g_variant_builder_end (&builder));
166 3 : g_dbus_connection_emit_signal (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (self->skeleton)),
167 1 : self->caller,
168 1 : g_dbus_interface_skeleton_get_object_path (G_DBUS_INTERFACE_SKELETON (self->skeleton)),
169 : "org.freedesktop.Secret.Prompt", "Completed",
170 : g_variant_new ("(b@v)", dismissed, variant),
171 : NULL);
172 :
173 : /* Fire off the next item in the unlock prompt queue */
174 1 : other = g_queue_pop_head (&unlock_prompt_queue);
175 1 : if (other != NULL) {
176 0 : perform_next_unlock (other);
177 0 : g_object_unref (other);
178 : }
179 :
180 1 : return TRUE;
181 : }
182 :
183 : static void
184 1 : on_unlock_complete (GObject *object, GAsyncResult *res, gpointer user_data)
185 : {
186 1 : GkdSecretUnlock *self = GKD_SECRET_UNLOCK (user_data);
187 : GkdSecretUnlock *other;
188 : GckObject *cred;
189 1 : GError *error = NULL;
190 :
191 : /* We should be at the front of the unlock queue, pop ourselves */
192 1 : other = g_queue_pop_head (&unlock_prompt_queue);
193 1 : if (other == self)
194 1 : g_object_unref (other);
195 : else
196 0 : g_warning ("unlock prompt queue is out of sync with prompts");
197 :
198 : /* Now process the results */
199 1 : cred = gck_session_create_object_finish (GCK_SESSION (object), res, &error);
200 :
201 : /* Successfully authentication */
202 1 : if (cred) {
203 1 : g_object_unref (cred);
204 1 : emit_collection_unlocked (self, self->current);
205 1 : g_array_append_val (self->results, self->current);
206 1 : self->current = NULL;
207 1 : perform_next_unlock (self);
208 :
209 : /* The user cancelled the protected auth prompt */
210 0 : } else if (g_error_matches (error, GCK_ERROR, CKR_PIN_INCORRECT)) {
211 0 : g_free (self->current);
212 0 : self->current = NULL;
213 0 : mark_as_complete (self, TRUE);
214 :
215 : /* The operation was cancelled via Dismiss call */
216 0 : } else if (g_error_matches (error, GCK_ERROR, CKR_CANCEL)) {
217 : /* Should have been the result of a dismiss */
218 0 : g_return_if_fail (self->completed);
219 :
220 : /* Another error, something's broken */
221 : } else {
222 0 : g_warning ("couldn't create credential for collection: %s",
223 : egg_error_message (error));
224 : }
225 :
226 1 : g_clear_error (&error);
227 :
228 : /* refed for async call */
229 1 : g_object_unref (self);
230 : }
231 :
232 : static void
233 2 : perform_next_unlock (GkdSecretUnlock *self)
234 : {
235 2 : GckBuilder builder = GCK_BUILDER_INIT;
236 : GckObject *collection;
237 : GckSession *session;
238 : gboolean locked;
239 : gboolean proceed;
240 : gchar *objpath;
241 :
242 : for (;;) {
243 2 : g_assert (!self->current);
244 2 : objpath = g_queue_pop_head (self->queued);
245 :
246 : /* Nothing more to prompt for? */
247 2 : if (!objpath) {
248 1 : mark_as_complete (self, FALSE);
249 1 : break;
250 : }
251 :
252 : /* Find the collection, make sure it's still around */
253 1 : collection = lookup_collection (self, objpath);
254 1 : if (collection == NULL) {
255 0 : g_free (objpath);
256 0 : continue;
257 : }
258 :
259 1 : if (!check_locked_collection (collection, &locked)) {
260 0 : g_object_unref (collection);
261 0 : g_free (objpath);
262 0 : continue;
263 :
264 1 : } else if (!locked) {
265 0 : g_array_append_val (self->results, objpath);
266 0 : g_object_unref (collection);
267 0 : continue;
268 : }
269 :
270 : /* Add ourselves to the unlock prompt queue */
271 1 : proceed = g_queue_is_empty (&unlock_prompt_queue);
272 1 : g_queue_push_tail (&unlock_prompt_queue, g_object_ref (self));
273 :
274 : /*
275 : * Proceed with this unlock request. The on_unlock_complete callback
276 : * pops us back off the unlock prompt queue
277 : */
278 1 : if (proceed) {
279 1 : common_unlock_attributes (&builder, collection);
280 1 : gck_builder_add_boolean (&builder, CKA_GNOME_TRANSIENT, TRUE);
281 1 : gck_builder_add_data (&builder, CKA_VALUE, NULL, 0);
282 :
283 1 : session = gkd_secret_service_get_pkcs11_session (self->service, self->caller);
284 1 : gck_session_create_object_async (session, gck_builder_end (&builder),
285 : self->cancellable, on_unlock_complete,
286 : g_object_ref (self));
287 1 : self->current = objpath;
288 1 : break;
289 : }
290 :
291 0 : g_object_unref (collection);
292 :
293 : /*
294 : * Already have one unlock request going on. Just wait around
295 : * and this function will be called again later.
296 : */
297 0 : if (!proceed) {
298 0 : g_queue_push_head (self->queued, objpath);
299 0 : break;
300 : }
301 : }
302 2 : }
303 :
304 : /* -----------------------------------------------------------------------------
305 : * DBUS
306 : */
307 :
308 : static gboolean
309 1 : prompt_method_prompt (GkdExportedPrompt *skeleton,
310 : GDBusMethodInvocation *invocation,
311 : gchar *window_id,
312 : GkdSecretUnlock *self)
313 : {
314 1 : if (!gkd_dbus_invocation_matches_caller (invocation, self->caller))
315 0 : return FALSE;
316 :
317 : /* Act as if this object no longer exists */
318 1 : if (self->completed)
319 0 : return FALSE;
320 :
321 : /* Prompt can only be called once */
322 1 : if (self->prompted) {
323 0 : g_dbus_method_invocation_return_error_literal (invocation,
324 : GKD_SECRET_ERROR,
325 : GKD_SECRET_ERROR_ALREADY_EXISTS,
326 : "This prompt has already been shown.");
327 0 : return TRUE;
328 : }
329 :
330 1 : gkd_secret_unlock_call_prompt (self, window_id);
331 :
332 1 : gkd_exported_prompt_complete_prompt (skeleton, invocation);
333 1 : return TRUE;
334 : }
335 :
336 : static gboolean
337 0 : prompt_method_dismiss (GkdExportedPrompt *skeleton,
338 : GDBusMethodInvocation *invocation,
339 : GkdSecretUnlock *self)
340 : {
341 0 : if (!gkd_dbus_invocation_matches_caller (invocation, self->caller))
342 0 : return FALSE;
343 :
344 : /* Act as if this object no longer exists */
345 0 : if (self->completed)
346 0 : return FALSE;
347 :
348 0 : g_cancellable_cancel (self->cancellable);
349 0 : mark_as_complete (self, TRUE);
350 :
351 0 : gkd_exported_prompt_complete_dismiss (skeleton, invocation);
352 0 : return TRUE;
353 : }
354 :
355 : /* -----------------------------------------------------------------------------
356 : * OBJECT
357 : */
358 :
359 : static void
360 1 : gkd_secret_unlock_init (GkdSecretUnlock *self)
361 : {
362 1 : self->queued = g_queue_new ();
363 1 : self->results = g_array_new (TRUE, TRUE, sizeof (gchar*));
364 1 : self->cancellable = g_cancellable_new ();
365 1 : }
366 :
367 : static GObject*
368 1 : gkd_secret_unlock_constructor (GType type, guint n_props, GObjectConstructParam *props)
369 : {
370 1 : GkdSecretUnlock *self = GKD_SECRET_UNLOCK (G_OBJECT_CLASS (gkd_secret_unlock_parent_class)->constructor(type, n_props, props));
371 1 : GError *error = NULL;
372 :
373 1 : g_return_val_if_fail (self, NULL);
374 1 : g_return_val_if_fail (self->caller, NULL);
375 1 : g_return_val_if_fail (self->service, NULL);
376 :
377 : /* Setup the path for the object */
378 1 : if (!self->object_path)
379 1 : self->object_path = g_strdup_printf (SECRET_PROMPT_PREFIX "/u%d", ++unique_prompt_number);
380 :
381 1 : self->skeleton = gkd_exported_prompt_skeleton_new ();
382 1 : g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (self->skeleton),
383 1 : gkd_secret_service_get_connection (self->service), self->object_path,
384 : &error);
385 :
386 1 : if (error != NULL) {
387 0 : g_warning ("could not register secret unlock prompt on session bus: %s", error->message);
388 0 : g_error_free (error);
389 : }
390 :
391 1 : g_signal_connect (self->skeleton, "handle-dismiss",
392 : G_CALLBACK (prompt_method_dismiss), self);
393 1 : g_signal_connect (self->skeleton, "handle-prompt",
394 : G_CALLBACK (prompt_method_prompt), self);
395 :
396 1 : return G_OBJECT (self);
397 : }
398 :
399 : static void
400 2 : gkd_secret_unlock_dispose (GObject *obj)
401 : {
402 2 : GkdSecretUnlock *self = GKD_SECRET_UNLOCK (obj);
403 :
404 2 : if (self->skeleton) {
405 1 : g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (self->skeleton));
406 1 : g_clear_object (&self->skeleton);
407 : }
408 :
409 2 : if (self->service) {
410 1 : g_object_remove_weak_pointer (G_OBJECT (self->service),
411 1 : (gpointer*)&(self->service));
412 1 : self->service = NULL;
413 : }
414 :
415 2 : G_OBJECT_CLASS (gkd_secret_unlock_parent_class)->dispose (obj);
416 2 : }
417 :
418 : static void
419 1 : gkd_secret_unlock_finalize (GObject *obj)
420 : {
421 1 : GkdSecretUnlock *self = GKD_SECRET_UNLOCK (obj);
422 :
423 1 : g_free (self->object_path);
424 1 : self->object_path = NULL;
425 :
426 1 : if (g_queue_find (&unlock_prompt_queue, self))
427 0 : g_warning ("unlock queue is not in sync with prompting");
428 :
429 1 : if (self->queued) {
430 1 : while (!g_queue_is_empty (self->queued))
431 0 : g_free (g_queue_pop_head (self->queued));
432 1 : g_queue_free (self->queued);
433 1 : self->queued = NULL;
434 : }
435 :
436 1 : if (self->results) {
437 1 : gkd_secret_unlock_reset_results (self);
438 1 : g_array_free (self->results, TRUE);
439 1 : self->results = NULL;
440 : }
441 :
442 1 : g_free (self->current);
443 1 : self->current = NULL;
444 :
445 1 : g_object_unref (self->cancellable);
446 1 : self->cancellable = NULL;
447 :
448 1 : g_assert (!self->object_path);
449 1 : g_assert (!self->service);
450 :
451 1 : g_free (self->caller);
452 1 : self->caller = NULL;
453 :
454 1 : g_free (self->window_id);
455 1 : self->window_id = NULL;
456 :
457 1 : G_OBJECT_CLASS (gkd_secret_unlock_parent_class)->finalize (obj);
458 1 : }
459 :
460 : static void
461 3 : gkd_secret_unlock_set_property (GObject *obj, guint prop_id, const GValue *value,
462 : GParamSpec *pspec)
463 : {
464 3 : GkdSecretUnlock *self = GKD_SECRET_UNLOCK (obj);
465 :
466 3 : switch (prop_id) {
467 1 : case PROP_CALLER:
468 1 : g_return_if_fail (!self->caller);
469 1 : self->caller = g_value_dup_string (value);
470 1 : break;
471 1 : case PROP_SERVICE:
472 1 : g_return_if_fail (!self->service);
473 1 : self->service = g_value_get_object (value);
474 1 : g_return_if_fail (self->service);
475 1 : g_object_add_weak_pointer (G_OBJECT (self->service),
476 1 : (gpointer*)&(self->service));
477 1 : break;
478 1 : case PROP_OBJECT_PATH:
479 1 : g_return_if_fail (!self->object_path);
480 1 : self->object_path = g_strdup (g_value_get_pointer (value));
481 1 : break;
482 0 : default:
483 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
484 0 : break;
485 : }
486 : }
487 :
488 : static void
489 2 : gkd_secret_unlock_get_property (GObject *obj, guint prop_id, GValue *value,
490 : GParamSpec *pspec)
491 : {
492 2 : GkdSecretUnlock *self = GKD_SECRET_UNLOCK (obj);
493 :
494 2 : switch (prop_id) {
495 0 : case PROP_CALLER:
496 0 : g_value_set_string (value, self->caller);
497 0 : break;
498 2 : case PROP_OBJECT_PATH:
499 2 : g_value_set_pointer (value, self->object_path);
500 2 : break;
501 0 : case PROP_SERVICE:
502 0 : g_value_set_object (value, self->service);
503 0 : break;
504 0 : default:
505 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
506 0 : break;
507 : }
508 2 : }
509 :
510 :
511 : static void
512 1 : gkd_secret_unlock_class_init (GkdSecretUnlockClass *klass)
513 : {
514 1 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
515 :
516 1 : gobject_class->constructor = gkd_secret_unlock_constructor;
517 1 : gobject_class->get_property = gkd_secret_unlock_get_property;
518 1 : gobject_class->set_property = gkd_secret_unlock_set_property;
519 1 : gobject_class->dispose = gkd_secret_unlock_dispose;
520 1 : gobject_class->finalize = gkd_secret_unlock_finalize;
521 :
522 1 : g_object_class_install_property (gobject_class, PROP_CALLER,
523 : g_param_spec_string ("caller", "Caller", "DBus caller name",
524 : NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY ));
525 :
526 1 : g_object_class_install_property (gobject_class, PROP_OBJECT_PATH,
527 : g_param_spec_pointer ("object-path", "Object Path", "DBus Object Path",
528 : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
529 :
530 1 : g_object_class_install_property (gobject_class, PROP_SERVICE,
531 : g_param_spec_object ("service", "Service", "Service which owns this prompt",
532 : GKD_SECRET_TYPE_SERVICE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
533 1 : }
534 :
535 : static void
536 1 : gkd_secret_dispatch_iface (GkdSecretDispatchIface *iface)
537 : {
538 1 : }
539 :
540 : /* -----------------------------------------------------------------------------
541 : * PUBLIC
542 : */
543 :
544 : GkdSecretUnlock*
545 1 : gkd_secret_unlock_new (GkdSecretService *service, const gchar *caller,
546 : const gchar *object_path)
547 : {
548 1 : return g_object_new (GKD_SECRET_TYPE_UNLOCK,
549 : "service", service,
550 : "caller", caller,
551 : "object-path", object_path,
552 : NULL);
553 : }
554 :
555 : void
556 1 : gkd_secret_unlock_queue (GkdSecretUnlock *self, const gchar *unlock_path)
557 : {
558 1 : gboolean locked = TRUE;
559 : GckObject *coll;
560 : gchar *path;
561 :
562 1 : g_return_if_fail (GKD_SECRET_IS_UNLOCK (self));
563 1 : g_return_if_fail (unlock_path);
564 :
565 1 : coll = lookup_collection (self, unlock_path);
566 1 : if (coll == NULL)
567 0 : return;
568 :
569 : /* Try to unlock with an empty password, which produces no prompt */
570 1 : if (gkd_secret_unlock_with_password (coll, (const guchar*)"", 0, NULL)) {
571 0 : locked = FALSE;
572 :
573 : }
574 :
575 1 : path = g_strdup (unlock_path);
576 1 : if (locked)
577 1 : g_queue_push_tail (self->queued, path);
578 : else
579 0 : g_array_append_val (self->results, path);
580 :
581 1 : g_object_unref (coll);
582 : }
583 :
584 : gboolean
585 1 : gkd_secret_unlock_have_queued (GkdSecretUnlock *self)
586 : {
587 1 : g_return_val_if_fail (GKD_SECRET_IS_UNLOCK (self), FALSE);
588 1 : return !g_queue_is_empty (self->queued) || self->current;
589 : }
590 :
591 : gchar**
592 1 : gkd_secret_unlock_get_results (GkdSecretUnlock *self, gint *n_results)
593 : {
594 1 : g_return_val_if_fail (GKD_SECRET_IS_UNLOCK (self), NULL);
595 1 : g_return_val_if_fail (n_results, NULL);
596 1 : *n_results = self->results->len;
597 1 : return (gchar**)self->results->data;
598 : }
599 :
600 : void
601 2 : gkd_secret_unlock_reset_results (GkdSecretUnlock *self)
602 : {
603 : gint i;
604 :
605 2 : g_return_if_fail (GKD_SECRET_IS_UNLOCK (self));
606 :
607 3 : for (i = 0; i < self->results->len; ++i)
608 1 : g_free (g_array_index (self->results, gchar*, i));
609 2 : g_array_set_size (self->results, 0);
610 : }
611 :
612 : void
613 1 : gkd_secret_unlock_call_prompt (GkdSecretUnlock *self, const gchar *window_id)
614 : {
615 1 : g_return_if_fail (GKD_SECRET_IS_UNLOCK (self));
616 1 : g_return_if_fail (!self->prompted);
617 :
618 1 : g_assert (!self->window_id);
619 1 : self->window_id = g_strdup (window_id);
620 :
621 1 : self->prompted = TRUE;
622 1 : perform_next_unlock (self);
623 : }
624 :
625 : gboolean
626 15 : gkd_secret_unlock_with_secret (GckObject *collection,
627 : GkdSecretSecret *master,
628 : GError **error)
629 : {
630 15 : GckBuilder builder = GCK_BUILDER_INIT;
631 : GckAttributes *attrs;
632 : GckObject *cred;
633 : gboolean locked;
634 :
635 15 : g_return_val_if_fail (GCK_IS_OBJECT (collection), FALSE);
636 15 : g_return_val_if_fail (master, FALSE);
637 :
638 : /* Shortcut if already unlocked */
639 15 : if (check_locked_collection (collection, &locked) && !locked)
640 0 : return TRUE;
641 :
642 15 : common_unlock_attributes (&builder, collection);
643 15 : gck_builder_add_boolean (&builder, CKA_GNOME_TRANSIENT, TRUE);
644 15 : gck_builder_add_boolean (&builder, CKA_TOKEN, TRUE);
645 15 : attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
646 :
647 15 : cred = gkd_secret_session_create_credential (master->session, NULL,
648 : attrs, master, error);
649 :
650 15 : gck_attributes_unref (attrs);
651 :
652 15 : if (cred != NULL)
653 15 : g_object_unref (cred);
654 15 : return (cred != NULL);
655 : }
656 :
657 : gboolean
658 1 : gkd_secret_unlock_with_password (GckObject *collection, const guchar *password,
659 : gsize n_password, GError **error_out)
660 : {
661 1 : GckBuilder builder = GCK_BUILDER_INIT;
662 1 : GError *error = NULL;
663 : GckSession *session;
664 : GckObject *cred;
665 : gboolean locked;
666 :
667 1 : g_return_val_if_fail (GCK_IS_OBJECT (collection), FALSE);
668 :
669 : /* Shortcut if already unlocked */
670 1 : if (check_locked_collection (collection, &locked) && !locked)
671 0 : return TRUE;
672 :
673 1 : session = gck_object_get_session (collection);
674 1 : g_return_val_if_fail (session, FALSE);
675 :
676 1 : gck_builder_init_full (&builder, GCK_BUILDER_SECURE_MEMORY);
677 1 : common_unlock_attributes (&builder, collection);
678 1 : gck_builder_add_boolean (&builder, CKA_GNOME_TRANSIENT, TRUE);
679 1 : gck_builder_add_boolean (&builder, CKA_TOKEN, TRUE);
680 1 : gck_builder_add_data (&builder, CKA_VALUE, password, n_password);
681 :
682 1 : cred = gck_session_create_object (session, gck_builder_end (&builder), NULL, &error);
683 1 : if (cred == NULL) {
684 1 : if (g_error_matches (error, GCK_ERROR, CKR_PIN_INCORRECT)) {
685 1 : g_set_error_literal (error_out, GKD_SECRET_DAEMON_ERROR,
686 : GKD_SECRET_DAEMON_ERROR_DENIED,
687 : "The password was incorrect.");
688 : } else {
689 0 : g_message ("couldn't create credential: %s", egg_error_message (error));
690 0 : g_set_error_literal (error_out, G_DBUS_ERROR,
691 : G_DBUS_ERROR_FAILED,
692 : "Couldn't use credentials");
693 : }
694 1 : g_clear_error (&error);
695 1 : return FALSE;
696 : }
697 :
698 0 : g_object_unref (cred);
699 0 : return TRUE;
700 : }
|