4
* Copyright (C) 2009 Stefan Walter
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.
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.
16
* You should have received a copy of the GNU Lesser General Public
17
* License along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
24
#include "gkd-login.h"
26
#include "egg/egg-secure-memory.h"
28
#include "pkcs11/gkd-pkcs11.h"
29
#include "pkcs11/pkcs11i.h"
31
#include <glib/gi18n.h>
35
static gint unlock_failures = 0;
38
note_that_unlock_failed (void)
40
g_atomic_int_inc (&unlock_failures);
44
note_that_unlock_succeeded (void)
46
g_atomic_int_set (&unlock_failures, 0);
50
gkd_login_did_unlock_fail (void)
52
return g_atomic_int_get (&unlock_failures) ? TRUE : FALSE;
56
module_instance (void)
58
GP11Module *module = gp11_module_new (gkd_pkcs11_get_base_functions ());
59
gp11_module_set_pool_sessions (module, FALSE);
60
gp11_module_set_auto_authenticate (module, FALSE);
61
g_return_val_if_fail (module, NULL);
66
open_and_login_session (GP11Slot *slot, CK_USER_TYPE user_type, GError **error)
71
g_return_val_if_fail (GP11_IS_SLOT (slot), NULL);
76
session = gp11_slot_open_session (slot, CKF_RW_SESSION, error);
77
if (session != NULL) {
78
if (!gp11_session_login (session, user_type, NULL, 0, error)) {
79
if ((*error)->code != CKR_USER_ALREADY_LOGGED_IN) {
80
g_object_unref (session);
90
lookup_login_session (GP11Module *module)
92
GP11Slot *slot = NULL;
99
g_assert (GP11_IS_MODULE (module));
102
* Find the right slot.
104
* TODO: This isn't necessarily the best way to do this.
105
* A good function could be added to gp11 library.
106
* But needs more thought on how to do this.
108
slots = gp11_module_get_slots (module, TRUE);
109
for (l = slots; !slot && l; l = g_list_next (l)) {
110
info = gp11_slot_get_info (l->data);
111
if (g_ascii_strcasecmp ("Secret Store", info->slot_description) == 0)
112
slot = g_object_ref (l->data);
113
gp11_slot_info_free (info);
115
gp11_list_unref_free (slots);
117
g_return_val_if_fail (slot, NULL);
119
session = open_and_login_session (slot, CKU_USER, &error);
120
if (session == NULL) {
121
g_warning ("couldn't open pkcs11 session for login: %s", error->message);
122
g_clear_error (&error);
125
g_object_unref (slot);
131
lookup_login_keyring (GP11Session *session)
133
GError *error = NULL;
134
GP11Object *login = NULL;
138
g_return_val_if_fail (GP11_IS_SESSION (session), NULL);
140
objects = gp11_session_find_objects (session, &error,
141
CKA_CLASS, GP11_ULONG, CKO_G_COLLECTION,
142
CKA_TOKEN, GP11_BOOLEAN, TRUE,
143
CKA_ID, (gsize)5, "login",
147
g_warning ("couldn't search for login keyring: %s", error->message);
148
g_clear_error (&error);
152
length = g_list_length (objects);
154
login = g_object_ref (objects->data);
155
gp11_object_set_session (login, session);
156
} else if (length > 1) {
157
g_warning ("more than one login keyring exists");
160
gp11_list_unref_free (objects);
165
create_login_keyring (GP11Session *session, GP11Object *cred, GError **error)
170
g_return_val_if_fail (GP11_IS_SESSION (session), NULL);
171
g_return_val_if_fail (GP11_IS_OBJECT (cred), NULL);
173
/* TRANSLATORS: This is the display label for the login keyring */
176
login = gp11_session_create_object (session, error,
177
CKA_CLASS, GP11_ULONG, CKO_G_COLLECTION,
178
CKA_ID, (gsize)5, "login",
179
CKA_LABEL, strlen (label), label,
180
CKA_G_CREDENTIAL, GP11_ULONG, gp11_object_get_handle (cred),
181
CKA_TOKEN, GP11_BOOLEAN, TRUE,
185
gp11_object_set_session (login, session);
190
create_credential (GP11Session *session, GP11Object *object,
191
const gchar *secret, GError **error)
193
GP11Attributes *attrs;
196
g_return_val_if_fail (GP11_IS_SESSION (session), NULL);
197
g_return_val_if_fail (!object || GP11_IS_OBJECT (object), NULL);
202
attrs = gp11_attributes_newv (CKA_CLASS, GP11_ULONG, CKO_G_CREDENTIAL,
203
CKA_VALUE, strlen (secret), secret,
204
CKA_GNOME_TRANSIENT, GP11_BOOLEAN, TRUE,
205
CKA_TOKEN, GP11_BOOLEAN, TRUE,
209
gp11_attributes_add_ulong (attrs, CKA_G_OBJECT,
210
gp11_object_get_handle (object));
212
cred = gp11_session_create_object_full (session, attrs, NULL, error);
213
gp11_attributes_unref (attrs);
216
gp11_object_set_session (cred, session);
222
unlock_or_create_login (GP11Module *module, const gchar *master)
224
GError *error = NULL;
225
GP11Session *session;
229
g_return_val_if_fail (GP11_IS_MODULE (module), FALSE);
230
g_return_val_if_fail (master, FALSE);
232
/* Find the login object */
233
session = lookup_login_session (module);
234
login = lookup_login_keyring (session);
236
/* Create credentials for login object */
237
cred = create_credential (session, login, master, &error);
239
/* Failure, bad password? */
241
if (login && error->code == CKR_PIN_INCORRECT)
242
note_that_unlock_failed ();
244
g_warning ("couldn't create login credential: %s", error->message);
245
g_clear_error (&error);
247
/* Non login keyring, create it */
249
login = create_login_keyring (session, cred, &error);
251
g_warning ("couldn't create login keyring: %s", error->message);
252
g_clear_error (&error);
255
/* The unlock succeeded yay */
257
note_that_unlock_succeeded ();
261
g_object_unref (cred);
263
g_object_unref (login);
265
g_object_unref (session);
267
return cred && login;
271
init_pin_for_uninitialized_slots (GP11Module *module, const gchar *master)
273
GError *error = NULL;
277
GP11Session *session;
279
g_return_val_if_fail (GP11_IS_MODULE (module), FALSE);
280
g_return_val_if_fail (master, FALSE);
282
slots = gp11_module_get_slots (module, TRUE);
283
for (l = slots; l; l = g_list_next (l)) {
284
info = gp11_slot_get_token_info (l->data);
285
initialize = (info && !(info->flags & CKF_USER_PIN_INITIALIZED));
288
session = open_and_login_session (l->data, CKU_SO, NULL);
289
if (session != NULL) {
290
if (gp11_session_init_pin (session, (const guchar*)master, strlen (master), &error)) {
291
gkd_login_attach_secret (info->label, master,
292
"manufacturer", info->manufacturer_id,
293
"serial-number", info->serial_number,
296
if (error->code != CKR_FUNCTION_NOT_SUPPORTED)
297
g_warning ("couldn't initialize slot with master password: %s", error->message);
298
g_clear_error (&error);
300
g_object_unref (session);
304
gp11_token_info_free (info);
306
gp11_list_unref_free (slots);
311
gkd_login_unlock (const gchar *master)
316
/* We don't support null or empty master passwords */
317
if (!master || !master[0])
320
module = module_instance ();
322
result = unlock_or_create_login (module, master);
324
init_pin_for_uninitialized_slots (module, master);
326
g_object_unref (module);
331
change_or_create_login (GP11Module *module, const gchar *original, const gchar *master)
333
GError *error = NULL;
334
GP11Session *session;
335
GP11Object *login = NULL;
336
GP11Object *ocred = NULL;
337
GP11Object *mcred = NULL;
338
gboolean success = FALSE;
340
g_return_val_if_fail (GP11_IS_MODULE (module), FALSE);
341
g_return_val_if_fail (original, FALSE);
342
g_return_val_if_fail (master, FALSE);
344
/* Find the login object */
345
session = lookup_login_session (module);
346
login = lookup_login_keyring (session);
348
/* Create the new credential we'll be changing to */
349
mcred = create_credential (session, NULL, master, &error);
351
g_warning ("couldn't create new login credential: %s", error->message);
352
g_clear_error (&error);
354
/* Create original credentials */
356
ocred = create_credential (session, login, original, &error);
358
if (error->code == CKR_PIN_INCORRECT) {
359
g_message ("couldn't change login master password, "
360
"original password was wrong: %s", error->message);
361
note_that_unlock_failed ();
363
g_warning ("couldn't create original login credential: %s", error->message);
365
g_clear_error (&error);
369
/* No keyring? try to create */
370
if (!login && mcred) {
371
login = create_login_keyring (session, mcred, &error);
373
g_warning ("couldn't create login keyring: %s", error->message);
374
g_clear_error (&error);
379
/* Change the master password */
380
} else if (login && ocred && mcred) {
381
if (!gp11_object_set (login, &error,
382
CKA_G_CREDENTIAL, GP11_ULONG, gp11_object_get_handle (mcred),
384
g_warning ("couldn't change login master password: %s", error->message);
385
g_clear_error (&error);
392
gp11_object_destroy (ocred, NULL);
393
g_object_unref (ocred);
396
g_object_unref (mcred);
398
g_object_unref (login);
400
g_object_unref (session);
406
set_pin_for_any_slots (GP11Module *module, const gchar *original, const gchar *master)
408
GError *error = NULL;
412
GP11Session *session;
414
g_return_val_if_fail (GP11_IS_MODULE (module), FALSE);
415
g_return_val_if_fail (original, FALSE);
416
g_return_val_if_fail (master, FALSE);
418
slots = gp11_module_get_slots (module, TRUE);
419
for (l = slots; l; l = g_list_next (l)) {
421
/* Set pin for any that are initialized, and not pap */
422
info = gp11_slot_get_token_info (l->data);
423
initialize = (info && (info->flags & CKF_USER_PIN_INITIALIZED));
426
session = open_and_login_session (l->data, CKU_USER, NULL);
427
if (session != NULL) {
428
if (gp11_session_set_pin (session, (const guchar*)original, strlen (original),
429
(const guchar*)master, strlen (master), &error)) {
430
gkd_login_attach_secret (info->label, master,
431
"manufacturer", info->manufacturer_id,
432
"serial-number", info->serial_number,
435
if (error->code != CKR_PIN_INCORRECT && error->code != CKR_FUNCTION_NOT_SUPPORTED)
436
g_warning ("couldn't change slot master password: %s", error->message);
437
g_clear_error (&error);
439
g_object_unref (session);
443
gp11_token_info_free (info);
445
gp11_list_unref_free (slots);
450
gkd_login_change_lock (const gchar *original, const gchar *master)
455
/* We don't support null or empty master passwords */
456
if (!master || !master[0])
458
if (original == NULL)
461
module = module_instance ();
463
result = change_or_create_login (module, original, master);
465
set_pin_for_any_slots (module, original, master);
467
g_object_unref (module);
472
gkd_login_is_usable (void)
475
GP11Session *session;
477
gboolean usable = FALSE;
481
module = module_instance ();
485
session = lookup_login_session (module);
487
login = lookup_login_keyring (session);
489
data = gp11_object_get_data (login, CKA_G_LOCKED, &n_data, NULL);
490
usable = (data && n_data == sizeof (CK_BBOOL) && !*((CK_BBOOL*)data));
492
g_object_unref (login);
494
g_object_unref (session);
497
g_object_unref (module);
502
string_attribute_list_va (va_list args, const gchar *name, GP11Attribute *attr)
504
GString *fields = g_string_sized_new(128);
507
while (name != NULL) {
508
g_string_append (fields, name);
509
g_string_append_c (fields, '\0');
510
g_string_append (fields, va_arg (args, const gchar*));
511
g_string_append_c (fields, '\0');
512
name = va_arg (args, const gchar*);
515
length = fields->len;
516
gp11_attribute_init (attr, CKA_G_FIELDS, g_string_free (fields, FALSE), length);
520
find_login_keyring_item (GP11Session *session, GP11Attribute *fields)
523
GP11Object *item = NULL;
525
GError *error = NULL;
529
g_return_val_if_fail (GP11_IS_SESSION (session), FALSE);
531
/* Create a search object */
532
search = gp11_session_create_object (session, &error,
533
CKA_CLASS, GP11_ULONG, CKO_G_SEARCH,
534
CKA_G_COLLECTION, (gsize)5, "login",
535
CKA_TOKEN, GP11_BOOLEAN, FALSE,
536
CKA_G_FIELDS, fields->length, fields->value,
540
g_warning ("couldn't create search for login keyring: %s", error->message);
541
g_clear_error (&error);
545
/* Get the data from the search */
546
gp11_object_set_session (search, session);
547
data = gp11_object_get_data (search, CKA_G_MATCHED, &n_data, &error);
548
gp11_object_destroy (search, NULL);
549
g_object_unref (search);
552
g_warning ("couldn't read search in login keyring: %s", error->message);
553
g_clear_error (&error);
557
n_data /= sizeof (CK_OBJECT_HANDLE);
558
objects = gp11_objects_from_handle_array (gp11_session_get_slot (session), data,
559
MIN (sizeof (CK_OBJECT_HANDLE), n_data));
563
item = g_object_ref (objects->data);
564
gp11_object_set_session (item, session);
567
gp11_list_unref_free (objects);
572
gkd_login_attach_secret (const gchar *label, const gchar *secret,
573
const gchar *first, ...)
575
GError *error = NULL;
576
GP11Attribute fields;
577
GP11Session *session;
584
label = _("Unnamed");
588
module = module_instance ();
589
session = lookup_login_session (module);
592
gp11_attribute_init_empty (&fields, CKA_G_FIELDS);
593
string_attribute_list_va (va, first, &fields);
596
display_name = g_strdup_printf (_("Unlock password for: %s"), label);
598
item = find_login_keyring_item (session, &fields);
600
gp11_object_set (item, &error,
601
CKA_LABEL, strlen (display_name), display_name,
602
CKA_VALUE, strlen (secret), secret,
605
item = gp11_session_create_object (session, &error,
606
CKA_TOKEN, GP11_BOOLEAN, TRUE,
607
CKA_CLASS, GP11_ULONG, CKO_SECRET_KEY,
608
CKA_LABEL, strlen (display_name), display_name,
609
CKA_VALUE, strlen (secret), secret,
610
CKA_G_COLLECTION, (gsize)5, "login",
611
CKA_G_FIELDS, fields.length, fields.value,
616
g_warning ("couldn't store secret in login keyring: %s", error->message);
617
g_clear_error (&error);
621
g_object_unref (item);
622
g_free (display_name);
623
gp11_attribute_clear (&fields);
624
g_object_unref (session);
625
g_object_unref (module);
629
gkd_login_lookup_secret (const gchar *first, ...)
631
GP11Attribute fields;
632
GP11Session *session;
635
gpointer data = NULL;
639
module = module_instance ();
640
session = lookup_login_session (module);
643
gp11_attribute_init_empty (&fields, CKA_G_FIELDS);
644
string_attribute_list_va (va, first, &fields);
647
item = find_login_keyring_item (session, &fields);
649
data = gp11_object_get_data_full (item, CKA_VALUE, egg_secure_realloc, NULL, &n_data, NULL);
650
if (data && !g_utf8_validate (data, n_data, NULL)) {
651
g_warning ("expected string, but found binary secret in login keyring");
652
egg_secure_clear (data, n_data);
653
egg_secure_free (data);
656
g_object_unref (item);
659
g_object_unref (session);
660
g_object_unref (module);
662
/* Memory returned from gp11_object_get_data is null terminated */
667
gkd_login_remove_secret (const gchar *first, ...)
669
GError *error = NULL;
670
GP11Attribute fields;
671
GP11Session *session;
676
module = module_instance ();
677
session = lookup_login_session (module);
680
gp11_attribute_init_empty (&fields, CKA_G_FIELDS);
681
string_attribute_list_va (va, first, &fields);
684
item = find_login_keyring_item (session, &fields);
686
if (!gp11_object_destroy (item, &error)) {
687
if (error->code != CKR_OBJECT_HANDLE_INVALID)
688
g_warning ("couldn't remove stored secret from login keyring: %s", error->message);
689
g_clear_error (&error);
691
g_object_unref (item);
694
g_object_unref (session);
695
g_object_unref (module);
699
gkd_login_attributes_for_secret (const gchar *first, ...)
701
GP11Attributes *attrs;
702
GP11Attribute *fields;
705
attrs = gp11_attributes_newv (CKA_CLASS, GP11_ULONG, CKO_SECRET_KEY,
706
CKA_G_COLLECTION, (gsize)5, "login",
710
fields = gp11_attributes_add_empty (attrs, CKA_G_FIELDS);
711
string_attribute_list_va (va, first, fields);