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 "gkm-secret-binary.h"
24 : #include "gkm-secret-collection.h"
25 : #include "gkm-secret-data.h"
26 : #include "gkm-secret-item.h"
27 : #include "gkm-secret-textual.h"
28 :
29 : #include "egg/egg-error.h"
30 :
31 : #include "gkm/gkm-attributes.h"
32 : #include "gkm/gkm-credential.h"
33 : #include "gkm/gkm-secret.h"
34 : #include "gkm/gkm-session.h"
35 : #include "gkm/gkm-transaction.h"
36 :
37 : #include <glib/gi18n.h>
38 :
39 : #include "pkcs11/pkcs11i.h"
40 :
41 : enum {
42 : PROP_0,
43 : PROP_FILENAME
44 : };
45 :
46 : struct _GkmSecretCollection {
47 : GkmSecretObject parent;
48 : GkmSecretData *sdata;
49 : GHashTable *items;
50 : gchar *filename;
51 : guint32 watermark;
52 : GArray *template;
53 : };
54 :
55 9283 : G_DEFINE_TYPE (GkmSecretCollection, gkm_secret_collection, GKM_TYPE_SECRET_OBJECT);
56 :
57 : /* Forward declarations */
58 : static void add_item (GkmSecretCollection *, GkmTransaction *, GkmSecretItem *);
59 : static void remove_item (GkmSecretCollection *, GkmTransaction *, GkmSecretItem *);
60 :
61 : /* -----------------------------------------------------------------------------
62 : * INTERNAL
63 : */
64 :
65 : static GkmDataResult
66 97 : load_collection_and_secret_data (GkmSecretCollection *self, GkmSecretData *sdata,
67 : const gchar *path)
68 : {
69 : GkmDataResult res;
70 97 : GError *error = NULL;
71 : guchar *data;
72 : gsize n_data;
73 :
74 : /* Read in the keyring */
75 97 : if (!g_file_get_contents (path, (gchar**)&data, &n_data, &error)) {
76 0 : g_message ("problem reading keyring: %s: %s",
77 : path, egg_error_message (error));
78 0 : g_clear_error (&error);
79 0 : return GKM_DATA_FAILURE;
80 : }
81 :
82 : /* Try to load from an encrypted file, and otherwise plain text */
83 97 : res = gkm_secret_binary_read (self, sdata, data, n_data);
84 97 : if (res == GKM_DATA_UNRECOGNIZED)
85 20 : res = gkm_secret_textual_read (self, sdata, data, n_data);
86 :
87 97 : g_free (data);
88 :
89 97 : return res;
90 : }
91 :
92 : static GkmCredential*
93 12 : lookup_unassociated_credential (GkmSession *session, CK_OBJECT_HANDLE handle)
94 : {
95 : GkmObject *object;
96 :
97 12 : if (gkm_session_lookup_readable_object (session, handle, &object) != CKR_OK)
98 0 : return NULL;
99 :
100 12 : if (gkm_credential_get_object (GKM_CREDENTIAL (object)) != NULL)
101 0 : return NULL;
102 :
103 12 : return GKM_CREDENTIAL (object);
104 : }
105 :
106 : static gboolean
107 100 : find_unlocked_credential (GkmCredential *cred, GkmObject *object, gpointer user_data)
108 : {
109 100 : CK_OBJECT_HANDLE *result = user_data;
110 :
111 100 : g_return_val_if_fail (!*result, FALSE);
112 :
113 100 : if (gkm_credential_peek_data (cred, GKM_TYPE_SECRET_DATA)) {
114 97 : *result = gkm_object_get_handle (GKM_OBJECT (cred));
115 97 : return TRUE;
116 : }
117 :
118 3 : return FALSE;
119 : }
120 :
121 : static gboolean
122 28 : find_unlocked_secret_data (GkmCredential *cred, GkmObject *object, gpointer user_data)
123 : {
124 28 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (object);
125 28 : GkmSecretData **result = user_data;
126 :
127 28 : g_return_val_if_fail (!*result, FALSE);
128 :
129 28 : *result = gkm_credential_pop_data (cred, GKM_TYPE_SECRET_DATA);
130 28 : if (*result) {
131 21 : g_return_val_if_fail (*result == self->sdata, FALSE);
132 21 : return TRUE;
133 : }
134 :
135 7 : return FALSE;
136 : }
137 :
138 : static void
139 518 : track_secret_data (GkmSecretCollection *self, GkmSecretData *data)
140 : {
141 518 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (self));
142 :
143 518 : if (self->sdata)
144 33 : g_object_remove_weak_pointer (G_OBJECT (self->sdata),
145 33 : (gpointer*)&(self->sdata));
146 518 : self->sdata = data;
147 518 : if (self->sdata)
148 153 : g_object_add_weak_pointer (G_OBJECT (self->sdata),
149 153 : (gpointer*)&(self->sdata));
150 : }
151 :
152 : static void
153 121 : each_value_to_list (gpointer key, gpointer value, gpointer user_data)
154 : {
155 121 : GList **list = user_data;
156 121 : *list = g_list_prepend (*list, value);
157 121 : }
158 :
159 : static void
160 119 : expose_each_item (gpointer key, gpointer value, gpointer user_data)
161 : {
162 119 : gboolean expose = GPOINTER_TO_INT (user_data);
163 119 : gkm_object_expose (value, expose);
164 119 : }
165 :
166 : static gboolean
167 8 : complete_add (GkmTransaction *transaction, GkmSecretCollection *self, GkmSecretItem *item)
168 : {
169 8 : if (gkm_transaction_get_failed (transaction))
170 1 : remove_item (self, NULL, item);
171 8 : g_object_unref (item);
172 8 : return TRUE;
173 : }
174 :
175 : static void
176 2181 : add_item (GkmSecretCollection *self, GkmTransaction *transaction, GkmSecretItem *item)
177 : {
178 : const gchar *identifier;
179 : guint32 number;
180 :
181 2181 : g_assert (GKM_IS_SECRET_COLLECTION (self));
182 2181 : g_assert (GKM_IS_SECRET_ITEM (item));
183 :
184 2181 : identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (item));
185 2181 : g_return_if_fail (identifier);
186 :
187 : /* Make note of the highest numeric identifier, for later use */
188 2181 : number = strtoul (identifier, NULL, 10);
189 2181 : if (number > self->watermark)
190 136 : self->watermark = number;
191 :
192 4362 : g_hash_table_replace (self->items, g_strdup (identifier), g_object_ref (item));
193 :
194 2181 : if (gkm_object_is_exposed (GKM_OBJECT (self)))
195 2006 : gkm_object_expose_full (GKM_OBJECT (item), transaction, TRUE);
196 2181 : if (transaction)
197 8 : gkm_transaction_add (transaction, self, (GkmTransactionFunc)complete_add,
198 : g_object_ref (item));
199 :
200 : }
201 :
202 : static gboolean
203 4 : complete_remove (GkmTransaction *transaction, GkmSecretCollection *self, GkmSecretItem *item)
204 : {
205 4 : if (gkm_transaction_get_failed (transaction))
206 1 : add_item (self, NULL, item);
207 4 : g_object_unref (item);
208 4 : return TRUE;
209 : }
210 :
211 : static void
212 12 : remove_item (GkmSecretCollection *self, GkmTransaction *transaction, GkmSecretItem *item)
213 : {
214 : const gchar *identifier;
215 :
216 12 : g_assert (GKM_IS_SECRET_COLLECTION (self));
217 12 : g_assert (GKM_IS_SECRET_ITEM (item));
218 :
219 12 : identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (item));
220 12 : g_return_if_fail (identifier);
221 :
222 12 : g_object_ref (item);
223 :
224 12 : g_hash_table_remove (self->items, identifier);
225 :
226 12 : gkm_object_expose_full (GKM_OBJECT (item), transaction, FALSE);
227 12 : if (transaction)
228 4 : gkm_transaction_add (transaction, self, (GkmTransactionFunc)complete_remove,
229 : g_object_ref (item));
230 :
231 12 : g_object_unref (item);
232 : }
233 :
234 : static GkmObject*
235 12 : factory_create_collection (GkmSession *session, GkmTransaction *transaction,
236 : CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
237 : {
238 12 : GkmSecretCollection *collection = NULL;
239 : CK_OBJECT_HANDLE handle;
240 : CK_ATTRIBUTE *attr;
241 : GkmManager *manager;
242 : GkmModule *module;
243 12 : gchar *identifier = NULL;
244 : GkmSecretData *sdata;
245 12 : gchar *label = NULL;
246 : GkmCredential *cred;
247 : gboolean is_token;
248 : CK_RV rv;
249 :
250 12 : g_return_val_if_fail (GKM_IS_TRANSACTION (transaction), NULL);
251 12 : g_return_val_if_fail (attrs || !n_attrs, NULL);
252 :
253 12 : manager = gkm_manager_for_template (attrs, n_attrs, session);
254 12 : module = gkm_session_get_module (session);
255 :
256 : /* Must have a credential, which is not associated with an object yet */
257 12 : if (!gkm_attributes_find_ulong (attrs, n_attrs, CKA_G_CREDENTIAL, &handle)) {
258 0 : gkm_transaction_fail (transaction, CKR_TEMPLATE_INCOMPLETE);
259 0 : return NULL;
260 : }
261 :
262 12 : cred = lookup_unassociated_credential (session, handle);
263 12 : if (cred == NULL) {
264 0 : gkm_transaction_fail (transaction, CKR_ATTRIBUTE_VALUE_INVALID);
265 0 : return NULL;
266 : }
267 :
268 : /* The identifier */
269 12 : attr = gkm_attributes_find (attrs, n_attrs, CKA_ID);
270 12 : if (attr != NULL) {
271 2 : gkm_attribute_consume (attr);
272 2 : rv = gkm_attribute_get_string (attr, &identifier);
273 2 : if (rv != CKR_OK) {
274 0 : gkm_transaction_fail (transaction, rv);
275 0 : return NULL;
276 : }
277 :
278 : /* Try to find the collection with that identifier */
279 2 : if (!gkm_attributes_find_boolean (attrs, n_attrs, CKA_TOKEN, &is_token))
280 0 : collection = gkm_secret_collection_find (session, attr,
281 : gkm_module_get_manager (module),
282 : gkm_session_get_manager (session), NULL);
283 2 : else if (is_token)
284 2 : collection = gkm_secret_collection_find (session, attr,
285 : gkm_module_get_manager (module), NULL);
286 : else
287 0 : collection = gkm_secret_collection_find (session, attr,
288 : gkm_session_get_manager (session), NULL);
289 :
290 : /* Already have one with this identifier? Just return that */
291 2 : if (collection != NULL) {
292 0 : gkm_session_complete_object_creation (session, transaction, GKM_OBJECT (collection),
293 : FALSE, attrs, n_attrs);
294 0 : return GKM_OBJECT (g_object_ref (collection));
295 : }
296 : }
297 :
298 : /* The label */
299 12 : attr = gkm_attributes_find (attrs, n_attrs, CKA_LABEL);
300 12 : if (attr != NULL) {
301 11 : gkm_attribute_consume (attr);
302 11 : rv = gkm_attribute_get_string (attr, &label);
303 11 : if (rv != CKR_OK) {
304 0 : gkm_transaction_fail (transaction, rv);
305 0 : return NULL;
306 : }
307 :
308 : /* No identifier? Use label */
309 11 : if (identifier == NULL)
310 18 : identifier = g_strdup (label);
311 : }
312 :
313 12 : if (!identifier || !identifier[0]) {
314 1 : g_free (identifier);
315 1 : identifier = g_strdup ("unnamed");
316 : }
317 :
318 12 : if (!label || !label[0]) {
319 1 : g_free (label);
320 1 : if (identifier) {
321 2 : label = g_strdup (identifier);
322 : } else {
323 : /* TRANSLATORS: This is the label for an keyring created without a label */
324 0 : label = g_strdup (_("Unnamed"));
325 : }
326 : }
327 :
328 12 : g_strdelimit (identifier, ":/\\<>|\t\n\r\v ", '_');
329 12 : collection = g_object_new (GKM_TYPE_SECRET_COLLECTION,
330 : "module", gkm_session_get_module (session),
331 : "identifier", identifier,
332 : "manager", manager,
333 : "label", label,
334 : NULL);
335 :
336 12 : gkm_secret_object_mark_created (GKM_SECRET_OBJECT (collection));
337 12 : g_free (identifier);
338 12 : g_free (label);
339 :
340 12 : gkm_credential_connect (cred, GKM_OBJECT (collection));
341 12 : sdata = g_object_new (GKM_TYPE_SECRET_DATA, NULL);
342 12 : gkm_credential_set_data (cred, GKM_TYPE_SECRET_DATA, sdata);
343 12 : gkm_secret_data_set_master (sdata, gkm_credential_get_secret (cred));
344 12 : track_secret_data (collection, sdata);
345 12 : g_object_unref (sdata);
346 :
347 12 : gkm_attributes_consume (attrs, n_attrs, CKA_G_CREDENTIAL, G_MAXULONG);
348 12 : gkm_session_complete_object_creation (session, transaction, GKM_OBJECT (collection),
349 : TRUE, attrs, n_attrs);
350 12 : return GKM_OBJECT (collection);
351 : }
352 :
353 : static gboolean
354 0 : complete_master_password (GkmTransaction *transaction, GObject *object, gpointer user_data)
355 : {
356 0 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (object);
357 0 : GkmSecret *previous = user_data;
358 :
359 0 : if (gkm_transaction_get_failed (transaction)) {
360 0 : if (self->sdata)
361 0 : gkm_secret_data_set_master (self->sdata, previous);
362 : }
363 :
364 0 : if (previous)
365 0 : g_object_unref (previous);
366 :
367 0 : return TRUE;
368 : }
369 :
370 : static void
371 0 : change_master_password (GkmSecretCollection *self, GkmTransaction *transaction,
372 : GkmCredential *cred)
373 : {
374 : GkmSecret *previous;
375 :
376 0 : g_assert (GKM_IS_SECRET_COLLECTION (self));
377 0 : g_assert (GKM_IS_TRANSACTION (transaction));
378 0 : g_assert (GKM_IS_CREDENTIAL (cred));
379 :
380 0 : if (!self->sdata) {
381 0 : gkm_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN);
382 0 : return;
383 : }
384 :
385 0 : previous = gkm_secret_data_get_master (self->sdata);
386 0 : if (previous != NULL)
387 0 : g_object_ref (previous);
388 :
389 0 : gkm_credential_connect (cred, GKM_OBJECT (self));
390 0 : gkm_credential_set_data (cred, GKM_TYPE_SECRET_DATA, self->sdata);
391 0 : gkm_secret_data_set_master (self->sdata, gkm_credential_get_secret (cred));
392 :
393 0 : gkm_transaction_add (transaction, self, complete_master_password, previous);
394 : }
395 :
396 : /* -----------------------------------------------------------------------------
397 : * OBJECT
398 : */
399 :
400 : static CK_RV
401 1115 : gkm_secret_collection_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr)
402 : {
403 1115 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (base);
404 : const gchar *identifier;
405 : GkmSecret *master;
406 :
407 1115 : switch (attr->type) {
408 416 : case CKA_CLASS:
409 416 : return gkm_attribute_set_ulong (attr, CKO_G_COLLECTION);
410 2 : case CKA_G_CREDENTIAL_TEMPLATE:
411 2 : return gkm_attribute_set_template (attr, self->template);
412 16 : case CKA_G_LOGIN_COLLECTION:
413 16 : identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (base));
414 16 : g_return_val_if_fail (identifier, CKR_GENERAL_ERROR);
415 16 : return gkm_attribute_set_bool (attr, g_str_equal (identifier, "login"));
416 0 : case CKA_TRUSTED:
417 0 : if (!self->sdata)
418 0 : return gkm_attribute_set_bool (attr, CK_FALSE);
419 0 : master = gkm_secret_data_get_master (self->sdata);
420 0 : return gkm_attribute_set_bool (attr, (master && !gkm_secret_is_trivially_weak (master)));
421 : }
422 681 : return GKM_OBJECT_CLASS (gkm_secret_collection_parent_class)->get_attribute (base, session, attr);
423 : }
424 :
425 : static void
426 3 : gkm_secret_collection_set_attribute (GkmObject *object, GkmSession *session,
427 : GkmTransaction *transaction, CK_ATTRIBUTE *attr)
428 : {
429 3 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (object);
430 3 : CK_OBJECT_HANDLE handle = 0;
431 : GkmCredential *cred;
432 : GArray *template;
433 : CK_RV rv;
434 :
435 3 : switch (attr->type) {
436 0 : case CKA_G_CREDENTIAL:
437 0 : gkm_credential_for_each (session, GKM_OBJECT (self),
438 : find_unlocked_credential, &handle);
439 0 : if (handle == 0) {
440 0 : gkm_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN);
441 2 : return;
442 : }
443 0 : rv = gkm_attribute_get_ulong (attr, &handle);
444 0 : if (rv != CKR_OK) {
445 0 : gkm_transaction_fail (transaction, rv);
446 0 : return;
447 : }
448 0 : cred = lookup_unassociated_credential (session, handle);
449 0 : if (cred == NULL) {
450 0 : gkm_transaction_fail (transaction, CKR_ATTRIBUTE_VALUE_INVALID);
451 0 : return;
452 : }
453 0 : change_master_password (self, transaction, cred);
454 0 : return;
455 2 : case CKA_G_CREDENTIAL_TEMPLATE:
456 2 : rv = gkm_attribute_get_template (attr, &template);
457 2 : if (rv != CKR_OK) {
458 0 : gkm_transaction_fail (transaction, rv);
459 0 : return;
460 : }
461 2 : gkm_template_free (self->template);
462 2 : self->template = template;
463 2 : return;
464 : };
465 :
466 1 : GKM_OBJECT_CLASS (gkm_secret_collection_parent_class)->set_attribute (object, session, transaction, attr);
467 : }
468 :
469 : static CK_RV
470 148 : gkm_secret_collection_real_unlock (GkmObject *obj, GkmCredential *cred)
471 : {
472 148 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (obj);
473 : GkmDataResult res;
474 : GkmSecretData *sdata;
475 : GkmSecret *master;
476 148 : CK_RV rv = CKR_GENERAL_ERROR;
477 :
478 148 : master = gkm_credential_get_secret (cred);
479 :
480 : /* Already unlocked, make sure pin matches */
481 148 : if (self->sdata) {
482 2 : if (!gkm_secret_equal (gkm_secret_data_get_master (self->sdata), master))
483 1 : return CKR_PIN_INCORRECT;
484 :
485 : /* Credential now tracks our secret data */
486 1 : gkm_credential_set_data (cred, GKM_TYPE_SECRET_DATA, self->sdata);
487 1 : return CKR_OK;
488 : }
489 :
490 : /* New secret data object, setup master password */
491 146 : sdata = g_object_new (GKM_TYPE_SECRET_DATA, NULL);
492 146 : gkm_secret_data_set_master (sdata, master);
493 :
494 : /* Load the data from a file, and decrypt if necessary */
495 146 : if (self->filename) {
496 29 : res = load_collection_and_secret_data (self, sdata, self->filename);
497 :
498 : /* No filename, password must be null */
499 : } else {
500 117 : if (gkm_secret_equals (master, NULL, 0))
501 116 : res = GKM_DATA_SUCCESS;
502 : else
503 1 : res = GKM_DATA_LOCKED;
504 : }
505 :
506 146 : switch (res) {
507 141 : case GKM_DATA_SUCCESS:
508 141 : gkm_credential_set_data (cred, GKM_TYPE_SECRET_DATA, sdata);
509 141 : track_secret_data (self, sdata);
510 141 : rv = CKR_OK;
511 141 : break;
512 5 : case GKM_DATA_LOCKED:
513 5 : rv = CKR_PIN_INCORRECT;
514 5 : break;
515 0 : case GKM_DATA_UNRECOGNIZED:
516 0 : g_message ("unrecognized or invalid keyring: %s", self->filename);
517 0 : rv = CKR_FUNCTION_FAILED;
518 0 : break;
519 0 : case GKM_DATA_FAILURE:
520 0 : g_message ("failed to read or parse keyring: %s", self->filename);
521 0 : rv = CKR_GENERAL_ERROR;
522 0 : break;
523 0 : default:
524 0 : g_assert_not_reached ();
525 : }
526 :
527 146 : g_object_unref (sdata);
528 146 : return rv;
529 : }
530 :
531 : static void
532 396 : gkm_secret_collection_expose (GkmObject *base, gboolean expose)
533 : {
534 396 : GKM_OBJECT_CLASS (gkm_secret_collection_parent_class)->expose_object (base, expose);
535 396 : g_hash_table_foreach (GKM_SECRET_COLLECTION (base)->items, expose_each_item, GINT_TO_POINTER (expose));
536 396 : }
537 :
538 : static gboolean
539 144 : gkm_secret_collection_real_is_locked (GkmSecretObject *obj, GkmSession *session)
540 : {
541 144 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (obj);
542 144 : return !gkm_secret_collection_unlocked_have (self, session);
543 : }
544 :
545 : static void
546 252 : gkm_secret_collection_init (GkmSecretCollection *self)
547 : {
548 252 : CK_ULONG idle = 0;
549 252 : CK_ULONG after = 0;
550 252 : CK_BBOOL token = CK_TRUE;
551 252 : CK_ATTRIBUTE attrs[] = {
552 : { CKA_TOKEN, &token, sizeof (token) },
553 : { CKA_GNOME_TRANSIENT, &token, sizeof (token) },
554 : { CKA_G_DESTRUCT_IDLE, &idle, sizeof (idle) },
555 : { CKA_G_DESTRUCT_AFTER, &after, sizeof (after) },
556 : };
557 :
558 252 : self->items = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
559 252 : self->template = gkm_template_new (attrs, G_N_ELEMENTS (attrs));
560 252 : }
561 :
562 : static void
563 252 : gkm_secret_collection_set_property (GObject *obj, guint prop_id, const GValue *value,
564 : GParamSpec *pspec)
565 : {
566 252 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (obj);
567 :
568 252 : switch (prop_id) {
569 252 : case PROP_FILENAME:
570 252 : gkm_secret_collection_set_filename (self, g_value_get_string (value));
571 252 : break;
572 0 : default:
573 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
574 0 : break;
575 : }
576 252 : }
577 :
578 : static void
579 0 : gkm_secret_collection_get_property (GObject *obj, guint prop_id, GValue *value,
580 : GParamSpec *pspec)
581 : {
582 0 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (obj);
583 :
584 0 : switch (prop_id) {
585 0 : case PROP_FILENAME:
586 0 : g_value_set_string (value, gkm_secret_collection_get_filename (self));
587 0 : break;
588 0 : default:
589 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
590 0 : break;
591 : }
592 0 : }
593 :
594 : static void
595 365 : gkm_secret_collection_dispose (GObject *obj)
596 : {
597 365 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (obj);
598 :
599 365 : track_secret_data (self, NULL);
600 365 : g_hash_table_remove_all (self->items);
601 :
602 365 : G_OBJECT_CLASS (gkm_secret_collection_parent_class)->dispose (obj);
603 365 : }
604 :
605 : static void
606 252 : gkm_secret_collection_finalize (GObject *obj)
607 : {
608 252 : GkmSecretCollection *self = GKM_SECRET_COLLECTION (obj);
609 :
610 252 : g_assert (self->sdata == NULL);
611 :
612 252 : g_hash_table_destroy (self->items);
613 252 : self->items = NULL;
614 :
615 252 : g_free (self->filename);
616 252 : self->filename = NULL;
617 :
618 252 : gkm_template_free (self->template);
619 252 : self->template = NULL;
620 :
621 252 : G_OBJECT_CLASS (gkm_secret_collection_parent_class)->finalize (obj);
622 252 : }
623 :
624 : static void
625 34 : gkm_secret_collection_class_init (GkmSecretCollectionClass *klass)
626 : {
627 34 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
628 34 : GkmObjectClass *gkm_class = GKM_OBJECT_CLASS (klass);
629 34 : GkmSecretObjectClass *secret_class = GKM_SECRET_OBJECT_CLASS (klass);
630 :
631 34 : gkm_secret_collection_parent_class = g_type_class_peek_parent (klass);
632 :
633 34 : gobject_class->set_property = gkm_secret_collection_set_property;
634 34 : gobject_class->get_property = gkm_secret_collection_get_property;
635 34 : gobject_class->dispose = gkm_secret_collection_dispose;
636 34 : gobject_class->finalize = gkm_secret_collection_finalize;
637 :
638 34 : gkm_class->get_attribute = gkm_secret_collection_get_attribute;
639 34 : gkm_class->set_attribute = gkm_secret_collection_set_attribute;
640 34 : gkm_class->unlock = gkm_secret_collection_real_unlock;
641 34 : gkm_class->expose_object = gkm_secret_collection_expose;
642 :
643 34 : secret_class->is_locked = gkm_secret_collection_real_is_locked;
644 :
645 34 : g_object_class_install_property (gobject_class, PROP_FILENAME,
646 : g_param_spec_string ("filename", "Filename", "Collection filename (without path)",
647 : NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
648 :
649 34 : gkm_secret_object_class_unique_identifiers (secret_class);
650 34 : }
651 :
652 : /* -----------------------------------------------------------------------------
653 : * PUBLIC
654 : */
655 :
656 : GkmFactory*
657 117 : gkm_secret_collection_get_factory (void)
658 : {
659 : static CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
660 :
661 : static CK_ATTRIBUTE attributes[] = {
662 : { CKA_CLASS, &klass, sizeof (klass) },
663 : };
664 :
665 : static GkmFactory factory = {
666 : attributes,
667 : G_N_ELEMENTS (attributes),
668 : factory_create_collection
669 : };
670 :
671 117 : return &factory;
672 : }
673 :
674 : const gchar*
675 71 : gkm_secret_collection_get_filename (GkmSecretCollection *self)
676 : {
677 71 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), NULL);
678 71 : return self->filename;
679 : }
680 :
681 : void
682 273 : gkm_secret_collection_set_filename (GkmSecretCollection *self, const gchar *filename)
683 : {
684 273 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (self));
685 :
686 273 : if (self->filename == filename)
687 192 : return;
688 81 : g_free (self->filename);
689 81 : self->filename = g_strdup (filename);
690 81 : g_object_notify (G_OBJECT (self), "filename");
691 : }
692 :
693 : GList*
694 160 : gkm_secret_collection_get_items (GkmSecretCollection *self)
695 : {
696 160 : GList *items = NULL;
697 160 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), NULL);
698 160 : g_hash_table_foreach (self->items, each_value_to_list, &items);
699 160 : return items;
700 : }
701 :
702 : GkmSecretItem*
703 195 : gkm_secret_collection_get_item (GkmSecretCollection *self, const gchar *identifier)
704 : {
705 195 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), NULL);
706 195 : g_return_val_if_fail (identifier, NULL);
707 195 : return g_hash_table_lookup (self->items, identifier);
708 : }
709 :
710 : gboolean
711 22 : gkm_secret_collection_has_item (GkmSecretCollection *self, GkmSecretItem *item)
712 : {
713 : const gchar *identifier;
714 :
715 22 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), FALSE);
716 22 : g_return_val_if_fail (GKM_IS_SECRET_ITEM (item), FALSE);
717 :
718 22 : identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (item));
719 22 : return g_hash_table_lookup (self->items, identifier) == item;
720 : }
721 :
722 : GkmSecretCollection*
723 10 : gkm_secret_collection_find (GkmSession *session, CK_ATTRIBUTE_PTR attr, ...)
724 : {
725 10 : CK_OBJECT_CLASS klass = CKO_G_COLLECTION;
726 10 : GkmSecretCollection *result = NULL;
727 : GkmManager *manager;
728 : CK_ATTRIBUTE attrs[2];
729 : GList *objects;
730 : va_list va;
731 :
732 10 : g_assert (attr);
733 :
734 10 : attrs[0].type = CKA_CLASS;
735 10 : attrs[0].ulValueLen = sizeof (klass);
736 10 : attrs[0].pValue = &klass;
737 10 : attrs[1].type = CKA_ID;
738 10 : attrs[1].ulValueLen = attr->ulValueLen;
739 10 : attrs[1].pValue = attr->pValue;
740 :
741 10 : va_start (va, attr);
742 20 : while (!result && (manager = va_arg (va, GkmManager*)) != NULL) {
743 10 : objects = gkm_manager_find_by_attributes (manager, session, attrs, 2);
744 10 : if (objects && GKM_IS_SECRET_COLLECTION (objects->data))
745 8 : result = objects->data;
746 10 : g_list_free (objects);
747 : }
748 10 : va_end (va);
749 :
750 10 : return result;
751 : }
752 :
753 : GkmSecretItem*
754 2172 : gkm_secret_collection_new_item (GkmSecretCollection *self, const gchar *identifier)
755 : {
756 : GkmSecretItem *item;
757 :
758 2172 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), NULL);
759 2172 : g_return_val_if_fail (identifier, NULL);
760 2172 : g_return_val_if_fail (!g_hash_table_lookup (self->items, identifier), NULL);
761 :
762 4344 : item = g_object_new (GKM_TYPE_SECRET_ITEM,
763 2172 : "module", gkm_object_get_module (GKM_OBJECT (self)),
764 2172 : "manager", gkm_object_get_manager (GKM_OBJECT (self)),
765 : "collection", self,
766 : "identifier", identifier,
767 : NULL);
768 :
769 2172 : add_item (self, NULL, item);
770 2172 : g_object_unref (item);
771 2172 : return item;
772 : }
773 :
774 : GkmSecretItem*
775 8 : gkm_secret_collection_create_item (GkmSecretCollection *self, GkmTransaction *transaction)
776 : {
777 : GkmSecretItem *item;
778 8 : gchar *identifier = NULL;
779 :
780 8 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), NULL);
781 8 : g_return_val_if_fail (transaction, NULL);
782 8 : g_return_val_if_fail (!gkm_transaction_get_failed (transaction), NULL);
783 :
784 : do {
785 8 : g_free (identifier);
786 8 : identifier = g_strdup_printf ("%d", ++(self->watermark));
787 8 : } while (g_hash_table_lookup (self->items, identifier));
788 :
789 16 : item = g_object_new (GKM_TYPE_SECRET_ITEM,
790 8 : "module", gkm_object_get_module (GKM_OBJECT (self)),
791 8 : "manager", gkm_object_get_manager (GKM_OBJECT (self)),
792 : "collection", self,
793 : "identifier", identifier,
794 : NULL);
795 :
796 8 : g_free (identifier);
797 8 : add_item (self, transaction, item);
798 8 : gkm_secret_object_mark_created (GKM_SECRET_OBJECT (item));
799 8 : g_object_unref (item);
800 8 : return item;
801 : }
802 :
803 : void
804 7 : gkm_secret_collection_remove_item (GkmSecretCollection *self, GkmSecretItem *item)
805 : {
806 7 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (self));
807 7 : g_return_if_fail (GKM_IS_SECRET_ITEM (item));
808 7 : g_return_if_fail (gkm_secret_collection_has_item (self, item));
809 :
810 7 : remove_item (self, NULL, item);
811 : }
812 :
813 : void
814 4 : gkm_secret_collection_destroy_item (GkmSecretCollection *self, GkmTransaction *transaction,
815 : GkmSecretItem *item)
816 : {
817 4 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (self));
818 4 : g_return_if_fail (GKM_IS_TRANSACTION (transaction));
819 4 : g_return_if_fail (GKM_IS_SECRET_ITEM (item));
820 4 : g_return_if_fail (gkm_secret_collection_has_item (self, item));
821 :
822 4 : remove_item (self, transaction, item);
823 : }
824 :
825 : gboolean
826 160 : gkm_secret_collection_unlocked_have (GkmSecretCollection *self, GkmSession *session)
827 : {
828 160 : CK_OBJECT_HANDLE handle = 0;
829 :
830 160 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), FALSE);
831 160 : g_return_val_if_fail (GKM_IS_SESSION (session), FALSE);
832 :
833 : /*
834 : * Look for credential objects that this session has access
835 : * to, and use those to find the secret data. If a secret data is
836 : * found, it should match the one we are tracking in self->sdata.
837 : */
838 :
839 160 : gkm_credential_for_each (session, GKM_OBJECT (self), find_unlocked_credential, &handle);
840 160 : return handle != 0;
841 : }
842 :
843 : GkmSecretData*
844 23 : gkm_secret_collection_unlocked_use (GkmSecretCollection *self, GkmSession *session)
845 : {
846 23 : GkmSecretData *sdata = NULL;
847 :
848 23 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), NULL);
849 23 : g_return_val_if_fail (GKM_IS_SESSION (session), NULL);
850 :
851 : /*
852 : * Look for credential objects that this session has access
853 : * to, and use those to find the secret data. If a secret data is
854 : * found, it should match the one we are tracking in self->sdata.
855 : */
856 :
857 23 : gkm_credential_for_each (session, GKM_OBJECT (self),
858 : find_unlocked_secret_data, &sdata);
859 :
860 23 : return sdata;
861 : }
862 :
863 : void
864 0 : gkm_secret_collection_unlocked_clear (GkmSecretCollection *self)
865 : {
866 : /*
867 : * TODO: This is a tough one to implement. I'm holding off and wondering
868 : * if we don't need it, perhaps? As it currently stands, what needs to happen
869 : * here is we need to find each and every credential that references the
870 : * secret data for this collection and completely delete those objects.
871 : */
872 0 : g_warning ("Clearing of secret data needs implementing");
873 0 : track_secret_data (self, NULL);
874 0 : }
875 :
876 : GkmDataResult
877 70 : gkm_secret_collection_load (GkmSecretCollection *self)
878 : {
879 70 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), GKM_DATA_FAILURE);
880 :
881 70 : if (!self->filename)
882 2 : return GKM_DATA_SUCCESS;
883 :
884 68 : return load_collection_and_secret_data (self, self->sdata, self->filename);
885 : }
886 :
887 : void
888 22 : gkm_secret_collection_save (GkmSecretCollection *self, GkmTransaction *transaction)
889 : {
890 : GkmSecret *master;
891 : GkmDataResult res;
892 : gpointer data;
893 : gsize n_data;
894 :
895 22 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (self));
896 22 : g_return_if_fail (GKM_IS_TRANSACTION (transaction));
897 22 : g_return_if_fail (!gkm_transaction_get_failed (transaction));
898 :
899 : /* HACK: We can't save unless the secret data was loaded */
900 22 : if (!self->sdata) {
901 0 : gkm_transaction_fail (transaction, CKR_USER_NOT_LOGGED_IN);
902 0 : return;
903 : }
904 :
905 : /* Don't save ourselves if no filename */
906 22 : if (!self->filename)
907 0 : return;
908 :
909 22 : master = gkm_secret_data_get_master (self->sdata);
910 22 : if (master == NULL || gkm_secret_equals (master, NULL, 0))
911 7 : res = gkm_secret_textual_write (self, self->sdata, &data, &n_data);
912 : else
913 15 : res = gkm_secret_binary_write (self, self->sdata, &data, &n_data);
914 :
915 22 : switch (res) {
916 0 : case GKM_DATA_FAILURE:
917 : case GKM_DATA_UNRECOGNIZED:
918 0 : g_warning ("couldn't prepare to write out keyring: %s", self->filename);
919 0 : gkm_transaction_fail (transaction, CKR_GENERAL_ERROR);
920 0 : break;
921 0 : case GKM_DATA_LOCKED:
922 0 : g_warning ("locked error while writing out keyring: %s", self->filename);
923 0 : gkm_transaction_fail (transaction, CKR_GENERAL_ERROR);
924 0 : break;
925 22 : case GKM_DATA_SUCCESS:
926 22 : gkm_transaction_write_file (transaction, self->filename, data, n_data);
927 22 : g_free (data);
928 22 : break;
929 0 : default:
930 0 : g_assert_not_reached ();
931 : };
932 : }
933 :
934 : void
935 2 : gkm_secret_collection_destroy (GkmSecretCollection *self, GkmTransaction *transaction)
936 : {
937 2 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (self));
938 2 : g_return_if_fail (GKM_IS_TRANSACTION (transaction));
939 2 : g_return_if_fail (!gkm_transaction_get_failed (transaction));
940 :
941 2 : gkm_object_expose_full (GKM_OBJECT (self), transaction, FALSE);
942 2 : if (self->filename)
943 2 : gkm_transaction_remove_file (transaction, self->filename);
944 : }
945 :
946 : gint
947 24 : gkm_secret_collection_get_lock_idle (GkmSecretCollection *self)
948 : {
949 : gulong value;
950 24 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), 0);
951 24 : if (!gkm_template_find_ulong (self->template, CKA_G_DESTRUCT_IDLE, &value))
952 3 : value = 0;
953 24 : return (gint)value;
954 : }
955 :
956 : void
957 0 : gkm_secret_collection_set_lock_idle (GkmSecretCollection *self, gint lock_timeout)
958 : {
959 0 : CK_ULONG value = (lock_timeout < 0) ? 0 : lock_timeout;
960 0 : CK_ATTRIBUTE attr = { CKA_G_DESTRUCT_IDLE, &value, sizeof (value) };
961 0 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (self));
962 0 : gkm_template_set (self->template, &attr);
963 : }
964 :
965 : gint
966 24 : gkm_secret_collection_get_lock_after (GkmSecretCollection *self)
967 : {
968 : gulong value;
969 24 : g_return_val_if_fail (GKM_IS_SECRET_COLLECTION (self), 0);
970 24 : if (!gkm_template_find_ulong (self->template, CKA_G_DESTRUCT_AFTER, &value))
971 3 : value = 0;
972 24 : return (gint)value;
973 : }
974 :
975 : void
976 0 : gkm_secret_collection_set_lock_after (GkmSecretCollection *self, gint lock_timeout)
977 : {
978 0 : CK_ULONG value = (lock_timeout < 0) ? 0 : lock_timeout;
979 0 : CK_ATTRIBUTE attr = { CKA_G_DESTRUCT_AFTER, &value, sizeof (value) };
980 0 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (self));
981 0 : gkm_template_set (self->template, &attr);
982 : }
|