Line data Source code
1 : /*
2 : * gnome-keyring
3 : *
4 : * Copyright (C) 2009 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-login.h"
24 :
25 : #include "daemon/gkd-pkcs11.h"
26 :
27 : #include "egg/egg-error.h"
28 : #include "egg/egg-secure-memory.h"
29 :
30 : #include "pkcs11/pkcs11i.h"
31 : #include "pkcs11/wrap-layer/gkm-wrap-layer.h"
32 :
33 : #include <gck/gck.h>
34 : #include <gcr/gcr-unlock-options.h>
35 :
36 : #include <glib/gi18n.h>
37 :
38 : #include <string.h>
39 :
40 0 : EGG_SECURE_DECLARE (gkd_login);
41 :
42 : static GList*
43 2 : module_instances (void)
44 : {
45 : CK_FUNCTION_LIST_PTR funcs;
46 : GckModule *module;
47 :
48 2 : funcs = gkd_pkcs11_get_base_functions ();
49 2 : g_return_val_if_fail (funcs != NULL && "instances", NULL);
50 :
51 2 : module = gck_module_new (funcs);
52 2 : g_return_val_if_fail (module, NULL);
53 2 : return g_list_append (NULL, module);
54 : }
55 :
56 : static GckSession*
57 6 : open_and_login_session (GckSlot *slot, CK_USER_TYPE user_type, GError **error)
58 : {
59 : GckSession *session;
60 6 : GError *err = NULL;
61 :
62 6 : g_return_val_if_fail (GCK_IS_SLOT (slot), NULL);
63 :
64 6 : if (!error)
65 4 : error = &err;
66 :
67 6 : session = gck_slot_open_session (slot, GCK_SESSION_READ_WRITE, NULL, error);
68 6 : if (session != NULL) {
69 6 : if (!gck_session_login (session, user_type, NULL, 0, NULL, error)) {
70 2 : if (g_error_matches (*error, GCK_ERROR, CKR_USER_ALREADY_LOGGED_IN)) {
71 2 : g_clear_error (error);
72 : } else {
73 0 : g_object_unref (session);
74 0 : session = NULL;
75 : }
76 : }
77 : }
78 :
79 6 : return session;
80 : }
81 :
82 : static GckSession*
83 2 : lookup_login_session (GList *modules)
84 : {
85 2 : GckSlot *slot = NULL;
86 2 : GError *error = NULL;
87 : GckSession *session;
88 2 : GList *owned = NULL;
89 :
90 2 : if (modules == NULL)
91 0 : modules = owned = module_instances ();
92 :
93 2 : slot = gck_modules_token_for_uri (modules, "pkcs11:token=Secret%20Store", &error);
94 2 : if (!slot) {
95 0 : if (error) {
96 0 : g_warning ("couldn't find secret store module: %s", egg_error_message (error));
97 0 : g_error_free (error);
98 : }
99 0 : g_list_free_full (owned, g_object_unref);
100 0 : return NULL;
101 : }
102 :
103 2 : session = open_and_login_session (slot, CKU_USER, &error);
104 2 : if (error) {
105 0 : g_warning ("couldn't open pkcs11 session for login: %s", egg_error_message (error));
106 0 : g_clear_error (&error);
107 : }
108 :
109 2 : g_object_unref (slot);
110 2 : g_list_free_full (owned, g_object_unref);
111 :
112 2 : return session;
113 : }
114 :
115 : static GckObject*
116 2 : lookup_login_keyring (GckSession *session)
117 : {
118 2 : GckBuilder builder = GCK_BUILDER_INIT;
119 2 : GError *error = NULL;
120 2 : GckObject *login = NULL;
121 : GList *objects;
122 : guint length;
123 :
124 2 : g_return_val_if_fail (GCK_IS_SESSION (session), NULL);
125 :
126 2 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_COLLECTION);
127 2 : gck_builder_add_boolean (&builder, CKA_TOKEN, TRUE);
128 2 : gck_builder_add_string (&builder, CKA_ID, "login");
129 :
130 2 : objects = gck_session_find_objects (session, gck_builder_end (&builder), NULL, &error);
131 :
132 2 : if (error) {
133 0 : g_warning ("couldn't search for login keyring: %s", egg_error_message (error));
134 0 : g_clear_error (&error);
135 0 : return NULL;
136 : }
137 :
138 2 : length = g_list_length (objects);
139 2 : if (length == 1)
140 0 : login = g_object_ref (objects->data);
141 2 : else if (length > 1)
142 0 : g_warning ("more than one login keyring exists");
143 :
144 2 : gck_list_unref_free (objects);
145 2 : return login;
146 : }
147 :
148 : static GckObject*
149 2 : create_login_keyring (GckSession *session, GckObject *cred, GError **error)
150 : {
151 2 : GckBuilder builder = GCK_BUILDER_INIT;
152 :
153 2 : g_return_val_if_fail (GCK_IS_SESSION (session), NULL);
154 2 : g_return_val_if_fail (GCK_IS_OBJECT (cred), NULL);
155 :
156 2 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_COLLECTION);
157 2 : gck_builder_add_string (&builder, CKA_ID, "login");
158 2 : gck_builder_add_ulong (&builder, CKA_G_CREDENTIAL, gck_object_get_handle (cred));
159 2 : gck_builder_add_boolean (&builder, CKA_TOKEN, TRUE);
160 :
161 : /* TRANSLATORS: This is the display label for the login keyring */
162 2 : gck_builder_add_string (&builder, CKA_LABEL, _("Login"));
163 :
164 2 : return gck_session_create_object (session, gck_builder_end (&builder), NULL, error);
165 : }
166 :
167 : static GckObject*
168 2 : create_credential (GckSession *session, GckObject *object,
169 : const gchar *secret, GError **error)
170 : {
171 2 : GckBuilder builder = GCK_BUILDER_INIT;
172 :
173 2 : g_return_val_if_fail (GCK_IS_SESSION (session), NULL);
174 2 : g_return_val_if_fail (!object || GCK_IS_OBJECT (object), NULL);
175 :
176 2 : if (!secret)
177 0 : secret = "";
178 :
179 2 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_CREDENTIAL);
180 2 : gck_builder_add_string (&builder, CKA_VALUE, secret);
181 2 : gck_builder_add_boolean (&builder, CKA_GNOME_TRANSIENT, TRUE);
182 2 : gck_builder_add_boolean (&builder, CKA_TOKEN, TRUE);
183 :
184 2 : if (object)
185 0 : gck_builder_add_ulong (&builder, CKA_G_OBJECT,
186 : gck_object_get_handle (object));
187 :
188 2 : return gck_session_create_object (session, gck_builder_end (&builder), NULL, error);
189 : }
190 :
191 : static gboolean
192 2 : unlock_or_create_login (GList *modules, const gchar *master)
193 : {
194 2 : GError *error = NULL;
195 : GckSession *session;
196 : GckObject *login;
197 : GckObject *cred;
198 :
199 2 : g_return_val_if_fail (master, FALSE);
200 :
201 : /* Find the login object */
202 2 : session = lookup_login_session (modules);
203 2 : login = lookup_login_keyring (session);
204 :
205 : /* Create credentials for login object */
206 2 : cred = create_credential (session, login, master, &error);
207 :
208 : /* Failure, bad password? */
209 2 : if (cred == NULL) {
210 0 : if (login && g_error_matches (error, GCK_ERROR, CKR_PIN_INCORRECT))
211 0 : gkm_wrap_layer_mark_login_unlock_failure (master);
212 : else
213 0 : g_warning ("couldn't create login credential: %s", egg_error_message (error));
214 0 : g_clear_error (&error);
215 :
216 : /* Non login keyring, create it */
217 2 : } else if (!login) {
218 2 : login = create_login_keyring (session, cred, &error);
219 2 : if (login == NULL && error) {
220 0 : g_warning ("couldn't create login keyring: %s", egg_error_message (error));
221 0 : g_clear_error (&error);
222 : }
223 :
224 : /* The unlock succeeded yay */
225 : } else {
226 0 : gkm_wrap_layer_mark_login_unlock_success ();
227 : }
228 :
229 2 : if (cred)
230 2 : g_object_unref (cred);
231 2 : if (login)
232 2 : g_object_unref (login);
233 2 : if (session)
234 2 : g_object_unref (session);
235 :
236 2 : return cred && login;
237 : }
238 :
239 : static gboolean
240 2 : init_pin_for_uninitialized_slots (GList *modules, const gchar *master)
241 : {
242 2 : GError *error = NULL;
243 : GList *slots, *l;
244 : gboolean initialize;
245 : GckTokenInfo *info;
246 : GckSession *session;
247 :
248 2 : g_return_val_if_fail (master, FALSE);
249 :
250 2 : slots = gck_modules_get_slots (modules, TRUE);
251 10 : for (l = slots; l; l = g_list_next (l)) {
252 8 : info = gck_slot_get_token_info (l->data);
253 8 : initialize = (info && !(info->flags & CKF_USER_PIN_INITIALIZED));
254 :
255 8 : if (initialize) {
256 4 : session = open_and_login_session (l->data, CKU_SO, NULL);
257 4 : if (session != NULL) {
258 4 : if (!gck_session_init_pin (session, (const guchar*)master, strlen (master), NULL, &error)) {
259 2 : if (!g_error_matches (error, GCK_ERROR, CKR_FUNCTION_NOT_SUPPORTED))
260 0 : g_warning ("couldn't initialize slot with master password: %s",
261 : egg_error_message (error));
262 2 : g_clear_error (&error);
263 : }
264 4 : g_object_unref (session);
265 : }
266 : }
267 :
268 8 : gck_token_info_free (info);
269 : }
270 2 : gck_list_unref_free (slots);
271 2 : return TRUE;
272 : }
273 :
274 : gboolean
275 2 : gkd_login_unlock (const gchar *master)
276 : {
277 : GList *modules;
278 : gboolean result;
279 :
280 : /* We don't support null as master password */
281 2 : if (!master)
282 0 : return FALSE;
283 :
284 2 : modules = module_instances ();
285 :
286 2 : result = unlock_or_create_login (modules, master);
287 2 : if (result == TRUE)
288 2 : init_pin_for_uninitialized_slots (modules, master);
289 :
290 2 : gck_list_unref_free (modules);
291 2 : return result;
292 : }
293 :
294 : static gboolean
295 0 : change_or_create_login (GList *modules, const gchar *original, const gchar *master)
296 : {
297 0 : GckBuilder builder = GCK_BUILDER_INIT;
298 0 : GError *error = NULL;
299 : GckSession *session;
300 0 : GckObject *login = NULL;
301 0 : GckObject *ocred = NULL;
302 0 : GckObject *mcred = NULL;
303 0 : gboolean success = FALSE;
304 :
305 0 : g_return_val_if_fail (original, FALSE);
306 0 : g_return_val_if_fail (master, FALSE);
307 :
308 : /* Find the login object */
309 0 : session = lookup_login_session (modules);
310 0 : login = lookup_login_keyring (session);
311 :
312 : /* Create the new credential we'll be changing to */
313 0 : mcred = create_credential (session, NULL, master, &error);
314 0 : if (mcred == NULL) {
315 0 : g_warning ("couldn't create new login credential: %s", egg_error_message (error));
316 0 : g_clear_error (&error);
317 :
318 : /* Create original credentials */
319 0 : } else if (login) {
320 0 : ocred = create_credential (session, login, original, &error);
321 0 : if (ocred == NULL) {
322 0 : if (g_error_matches (error, GCK_ERROR, CKR_PIN_INCORRECT)) {
323 0 : g_message ("couldn't change login master password, "
324 : "original password was wrong: %s",
325 : egg_error_message (error));
326 : } else {
327 0 : g_warning ("couldn't create original login credential: %s",
328 : egg_error_message (error));
329 : }
330 0 : g_clear_error (&error);
331 : }
332 : }
333 :
334 : /* No keyring? try to create */
335 0 : if (!login && mcred) {
336 0 : login = create_login_keyring (session, mcred, &error);
337 0 : if (login == NULL) {
338 0 : g_warning ("couldn't create login keyring: %s", egg_error_message (error));
339 0 : g_clear_error (&error);
340 : } else {
341 0 : success = TRUE;
342 : }
343 :
344 : /* Change the master password */
345 0 : } else if (login && ocred && mcred) {
346 0 : gck_builder_add_ulong (&builder, CKA_G_CREDENTIAL, gck_object_get_handle (mcred));
347 0 : if (!gck_object_set (login, gck_builder_end (&builder), NULL, &error)) {
348 0 : g_warning ("couldn't change login master password: %s", egg_error_message (error));
349 0 : g_clear_error (&error);
350 : } else {
351 0 : success = TRUE;
352 : }
353 : }
354 :
355 0 : if (ocred) {
356 0 : gck_object_destroy (ocred, NULL, NULL);
357 0 : g_object_unref (ocred);
358 : }
359 0 : if (mcred)
360 0 : g_object_unref (mcred);
361 0 : if (login)
362 0 : g_object_unref (login);
363 0 : if (session)
364 0 : g_object_unref (session);
365 :
366 0 : return success;
367 : }
368 :
369 : static gboolean
370 0 : set_pin_for_any_slots (GList *modules, const gchar *original, const gchar *master)
371 : {
372 0 : GError *error = NULL;
373 : GList *slots, *l;
374 : gboolean initialize;
375 : GckTokenInfo *info;
376 : GckSession *session;
377 :
378 0 : g_return_val_if_fail (original, FALSE);
379 0 : g_return_val_if_fail (master, FALSE);
380 :
381 0 : slots = gck_modules_get_slots (modules, TRUE);
382 0 : for (l = slots; l; l = g_list_next (l)) {
383 :
384 : /* Set pin for any that are initialized, and not pap */
385 0 : info = gck_slot_get_token_info (l->data);
386 0 : initialize = (info && (info->flags & CKF_USER_PIN_INITIALIZED));
387 :
388 0 : if (initialize) {
389 0 : session = open_and_login_session (l->data, CKU_USER, NULL);
390 0 : if (session != NULL) {
391 0 : if (!gck_session_set_pin (session, (const guchar*)original, strlen (original),
392 : (const guchar*)master, strlen (master), NULL, &error)) {
393 0 : if (!g_error_matches (error, GCK_ERROR, CKR_PIN_INCORRECT) &&
394 0 : !g_error_matches (error, GCK_ERROR, CKR_FUNCTION_NOT_SUPPORTED))
395 0 : g_warning ("couldn't change slot master password: %s",
396 : egg_error_message (error));
397 0 : g_clear_error (&error);
398 : }
399 0 : g_object_unref (session);
400 : }
401 : }
402 :
403 0 : gck_token_info_free (info);
404 : }
405 0 : gck_list_unref_free (slots);
406 0 : return TRUE;
407 : }
408 :
409 : gboolean
410 0 : gkd_login_change_lock (const gchar *original, const gchar *master)
411 : {
412 : GList *modules;
413 : gboolean result;
414 :
415 : /* We don't support null or empty master passwords */
416 0 : if (!master || !master[0])
417 0 : return FALSE;
418 0 : if (original == NULL)
419 0 : original = "";
420 :
421 0 : modules = module_instances ();
422 :
423 0 : result = change_or_create_login (modules, original, master);
424 0 : if (result == TRUE)
425 0 : set_pin_for_any_slots (modules, original, master);
426 :
427 0 : gck_list_unref_free (modules);
428 0 : return result;
429 : }
430 :
431 : gboolean
432 0 : gkd_login_available (GckSession *session)
433 : {
434 0 : GckBuilder builder = GCK_BUILDER_INIT;
435 0 : gboolean available = FALSE;
436 0 : GError *error = NULL;
437 : GList *objects;
438 :
439 0 : if (!session)
440 0 : session = lookup_login_session (NULL);
441 : else
442 0 : g_object_ref (session);
443 :
444 0 : if (session) {
445 0 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_COLLECTION);
446 0 : gck_builder_add_string (&builder, CKA_ID, "login");
447 0 : gck_builder_add_boolean (&builder, CKA_G_LOCKED, FALSE);
448 :
449 : /* Check if the login keyring is usable */
450 0 : objects = gck_session_find_objects (session, gck_builder_end (&builder), NULL, &error);
451 0 : if (error) {
452 0 : g_warning ("couldn't lookup login keyring: %s", error->message);
453 0 : g_clear_error (&error);
454 0 : } else if (objects) {
455 0 : available = TRUE;
456 : }
457 0 : g_list_free_full (objects, g_object_unref);
458 0 : g_object_unref (session);
459 : }
460 :
461 0 : return available;
462 : }
463 :
464 : static GList *
465 0 : find_saved_items (GckSession *session,
466 : GckAttributes *attrs)
467 : {
468 0 : GckBuilder builder = GCK_BUILDER_INIT;
469 0 : GError *error = NULL;
470 : const GckAttribute *attr;
471 : GckObject *search;
472 : GList *results;
473 : gpointer data;
474 : gsize n_data;
475 :
476 0 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_G_SEARCH);
477 0 : gck_builder_add_boolean (&builder, CKA_TOKEN, FALSE);
478 :
479 0 : attr = gck_attributes_find (attrs, CKA_G_COLLECTION);
480 0 : if (attr != NULL)
481 0 : gck_builder_add_attribute (&builder, attr);
482 :
483 0 : attr = gck_attributes_find (attrs, CKA_G_FIELDS);
484 0 : g_return_val_if_fail (attr != NULL, NULL);
485 0 : gck_builder_add_attribute (&builder, attr);
486 :
487 0 : search = gck_session_create_object (session, gck_builder_end (&builder), NULL, &error);
488 0 : if (search == NULL) {
489 0 : g_warning ("couldn't perform search for stored passphrases: %s",
490 : egg_error_message (error));
491 0 : g_clear_error (&error);
492 0 : return NULL;
493 : }
494 :
495 0 : data = gck_object_get_data (search, CKA_G_MATCHED, NULL, &n_data, &error);
496 0 : gck_object_destroy (search, NULL, NULL);
497 0 : g_object_unref (search);
498 :
499 0 : if (data == NULL) {
500 0 : g_warning ("couldn't retrieve list of stored passphrases: %s",
501 : egg_error_message (error));
502 0 : g_clear_error (&error);
503 0 : return NULL;
504 : }
505 :
506 0 : results = gck_objects_from_handle_array (session, data, n_data / sizeof (CK_ULONG));
507 :
508 0 : g_free (data);
509 0 : return results;
510 : }
511 :
512 : static gboolean
513 0 : fields_to_attribute (GckBuilder *builder,
514 : GHashTable *fields)
515 : {
516 0 : GString *concat = g_string_sized_new (128);
517 : const gchar *field;
518 : const gchar *value;
519 : GList *keys, *l;
520 :
521 0 : keys = g_hash_table_get_keys (fields);
522 0 : for (l = g_list_sort (keys, (GCompareFunc) g_strcmp0); l; l = l->next) {
523 0 : field = l->data;
524 0 : value = g_hash_table_lookup (fields, field);
525 0 : g_return_val_if_fail (value != NULL, FALSE);
526 :
527 : g_string_append (concat, field);
528 : g_string_append_c (concat, '\0');
529 : g_string_append (concat, value);
530 : g_string_append_c (concat, '\0');
531 : }
532 :
533 0 : gck_builder_add_data (builder, CKA_G_FIELDS, (const guchar *)concat->str, concat->len);
534 0 : g_string_free (concat, TRUE);
535 0 : return TRUE;
536 : }
537 :
538 : gchar *
539 0 : gkd_login_lookup_password (GckSession *session,
540 : const gchar *field,
541 : ...)
542 : {
543 : GHashTable *fields;
544 : const gchar *value;
545 : gchar *result;
546 : va_list va;
547 :
548 0 : fields = g_hash_table_new (g_str_hash, g_str_equal);
549 :
550 0 : va_start (va, field);
551 0 : while (field) {
552 0 : value = va_arg (va, const gchar *);
553 0 : g_hash_table_insert (fields, (gpointer)field, (gpointer)value);
554 0 : field = va_arg (va, const gchar *);
555 : }
556 0 : va_end (va);
557 :
558 0 : result = gkd_login_lookup_passwordv (session, fields);
559 0 : g_hash_table_unref (fields);
560 0 : return result;
561 : }
562 :
563 : gchar *
564 0 : gkd_login_lookup_passwordv (GckSession *session,
565 : GHashTable *fields)
566 : {
567 0 : GckBuilder builder = GCK_BUILDER_INIT;
568 : GckAttributes *attrs;
569 : GList *objects, *l;
570 0 : GError *error = NULL;
571 0 : gpointer data = NULL;
572 : gsize length;
573 :
574 0 : if (!session)
575 0 : session = lookup_login_session (NULL);
576 : else
577 0 : session = g_object_ref (session);
578 0 : if (!session)
579 0 : return NULL;
580 :
581 0 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_SECRET_KEY);
582 :
583 0 : if (!fields_to_attribute (&builder, fields))
584 0 : g_return_val_if_reached (FALSE);
585 :
586 0 : attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
587 0 : objects = find_saved_items (session, attrs);
588 0 : gck_attributes_unref (attrs);
589 :
590 : /* Return first password */
591 0 : data = NULL;
592 0 : for (l = objects; l; l = g_list_next (l)) {
593 0 : data = gck_object_get_data_full (l->data, CKA_VALUE, egg_secure_realloc, NULL, &length, &error);
594 0 : if (error) {
595 0 : if (!g_error_matches (error, GCK_ERROR, CKR_USER_NOT_LOGGED_IN))
596 0 : g_warning ("couldn't lookup password: %s", egg_error_message (error));
597 0 : g_clear_error (&error);
598 0 : data = NULL;
599 : } else {
600 0 : break;
601 : }
602 : }
603 :
604 0 : g_list_free_full (objects, g_object_unref);
605 0 : g_object_unref (session);
606 :
607 : /* Data is null terminated */
608 0 : return data;
609 : }
610 :
611 : void
612 0 : gkd_login_clear_password (GckSession *session,
613 : const gchar *field,
614 : ...)
615 : {
616 : GHashTable *fields;
617 : const gchar *value;
618 : va_list va;
619 :
620 0 : fields = g_hash_table_new (g_str_hash, g_str_equal);
621 :
622 0 : va_start (va, field);
623 0 : while (field) {
624 0 : value = va_arg (va, const gchar *);
625 0 : g_hash_table_insert (fields, (gpointer)field, (gpointer)value);
626 0 : field = va_arg (va, const gchar *);
627 : }
628 0 : va_end (va);
629 :
630 0 : gkd_login_clear_passwordv (session, fields);
631 0 : g_hash_table_unref (fields);
632 0 : }
633 :
634 : void
635 0 : gkd_login_clear_passwordv (GckSession *session,
636 : GHashTable *fields)
637 : {
638 0 : GckBuilder builder = GCK_BUILDER_INIT;
639 : GckAttributes *attrs;
640 : GList *objects, *l;
641 0 : GError *error = NULL;
642 :
643 0 : if (!session)
644 0 : session = lookup_login_session (NULL);
645 : else
646 0 : session = g_object_ref (session);
647 0 : if (!session)
648 0 : return;
649 :
650 0 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_SECRET_KEY);
651 :
652 0 : if (!fields_to_attribute (&builder, fields))
653 0 : g_return_if_reached ();
654 :
655 0 : attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
656 0 : objects = find_saved_items (session, attrs);
657 0 : gck_attributes_unref (attrs);
658 :
659 : /* Delete first item */
660 0 : for (l = objects; l; l = g_list_next (l)) {
661 0 : if (gck_object_destroy (l->data, NULL, &error))
662 0 : break; /* Only delete the first item */
663 0 : g_warning ("couldn't clear password: %s", error->message);
664 0 : g_clear_error (&error);
665 : }
666 :
667 0 : g_list_free_full (objects, g_object_unref);
668 0 : g_object_unref (session);
669 : }
670 :
671 : gboolean
672 0 : gkd_login_store_password (GckSession *session,
673 : const gchar *password,
674 : const gchar *label,
675 : const gchar *method,
676 : gint lifetime,
677 : const gchar *field,
678 : ...)
679 : {
680 : GHashTable *fields;
681 : const gchar *value;
682 : gboolean result;
683 : va_list va;
684 :
685 0 : fields = g_hash_table_new (g_str_hash, g_str_equal);
686 :
687 0 : va_start (va, field);
688 0 : while (field) {
689 0 : value = va_arg (va, const gchar *);
690 0 : g_hash_table_insert (fields, (gpointer)field, (gpointer)value);
691 0 : field = va_arg (va, const gchar *);
692 : }
693 0 : va_end (va);
694 :
695 0 : result = gkd_login_store_passwordv (session, password, label, method, lifetime, fields);
696 0 : g_hash_table_unref (fields);
697 0 : return result;
698 : }
699 :
700 : gboolean
701 0 : gkd_login_store_passwordv (GckSession *session,
702 : const gchar *password,
703 : const gchar *label,
704 : const gchar *method,
705 : gint lifetime,
706 : GHashTable *fields)
707 : {
708 0 : GckBuilder builder = GCK_BUILDER_INIT;
709 : GckAttributes *attrs;
710 : guchar *identifier;
711 : GList *previous;
712 0 : gboolean ret = FALSE;
713 : GckObject *item;
714 0 : GError *error = NULL;
715 : gsize length;
716 :
717 0 : if (!method)
718 0 : method = GCR_UNLOCK_OPTION_SESSION;
719 :
720 0 : if (!session)
721 0 : session = lookup_login_session (NULL);
722 : else
723 0 : session = g_object_ref (session);
724 0 : if (!session)
725 0 : return FALSE;
726 :
727 0 : if (!fields_to_attribute (&builder, fields))
728 0 : g_return_val_if_reached (FALSE);
729 :
730 0 : if (g_str_equal (method, GCR_UNLOCK_OPTION_ALWAYS)) {
731 0 : gck_builder_add_string (&builder, CKA_G_COLLECTION, "login");
732 :
733 : } else {
734 0 : if (g_str_equal (method, GCR_UNLOCK_OPTION_IDLE)) {
735 0 : gck_builder_add_boolean (&builder, CKA_GNOME_TRANSIENT, TRUE);
736 0 : gck_builder_add_ulong (&builder, CKA_G_DESTRUCT_IDLE, lifetime);
737 :
738 0 : } else if (g_str_equal (method, GCR_UNLOCK_OPTION_TIMEOUT)) {
739 0 : gck_builder_add_boolean (&builder, CKA_GNOME_TRANSIENT, TRUE);
740 0 : gck_builder_add_ulong (&builder, CKA_G_DESTRUCT_AFTER, lifetime);
741 :
742 0 : } else if (!g_str_equal (method, GCR_UNLOCK_OPTION_SESSION)) {
743 0 : g_message ("Unsupported gpg-cache-method setting: %s", method);
744 : }
745 0 : gck_builder_add_string (&builder, CKA_G_COLLECTION, "session");
746 : }
747 :
748 0 : gck_builder_add_boolean (&builder, CKA_TOKEN, TRUE);
749 0 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_SECRET_KEY);
750 :
751 : /* Find a previously stored object like this, and replace if so */
752 0 : attrs = gck_attributes_ref_sink (gck_builder_end (&builder));
753 0 : previous = find_saved_items (session, attrs);
754 0 : if (previous) {
755 0 : identifier = gck_object_get_data (previous->data, CKA_ID, NULL, &length, NULL);
756 0 : if (identifier != NULL)
757 0 : gck_builder_add_data (&builder, CKA_ID, identifier, length);
758 0 : g_free (identifier);
759 0 : g_list_free_full (previous, g_object_unref);
760 : }
761 :
762 : /* Put in the remainder of the attributes */
763 0 : gck_builder_add_all (&builder, attrs);
764 0 : gck_builder_add_string (&builder, CKA_VALUE, password);
765 0 : gck_builder_add_string (&builder, CKA_LABEL, label);
766 0 : gck_attributes_unref (attrs);
767 :
768 0 : item = gck_session_create_object (session, gck_builder_end (&builder), NULL, &error);
769 0 : if (item == NULL) {
770 0 : g_warning ("couldn't store password: %s", egg_error_message (error));
771 0 : g_clear_error (&error);
772 0 : ret = FALSE;
773 : } else {
774 0 : g_object_unref (item);
775 0 : ret = TRUE;
776 : }
777 :
778 0 : g_object_unref (session);
779 0 : return ret;
780 : }
|