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-collection.h"
24 : #include "gkm-secret-fields.h"
25 : #include "gkm-secret-item.h"
26 : #include "gkm-secret-search.h"
27 :
28 : #include "gkm/gkm-attributes.h"
29 : #include "gkm/gkm-manager.h"
30 : #include "gkm/gkm-module.h"
31 : #include "gkm/gkm-session.h"
32 : #include "gkm/gkm-transaction.h"
33 : #include "gkm/gkm-util.h"
34 :
35 : #include "pkcs11i.h"
36 :
37 : #include <glib/gi18n.h>
38 :
39 : enum {
40 : PROP_0,
41 : PROP_COLLECTION_ID,
42 : PROP_FIELDS,
43 : PROP_SCHEMA_NAME
44 : };
45 :
46 : struct _GkmSecretSearch {
47 : GkmObject parent;
48 : gchar *collection_id;
49 : GHashTable *fields;
50 : gchar *schema_name;
51 : GList *managers;
52 : GHashTable *objects;
53 : };
54 :
55 2577 : G_DEFINE_TYPE (GkmSecretSearch, gkm_secret_search, GKM_TYPE_OBJECT);
56 :
57 : static gboolean
58 2109 : match_object_against_criteria (GkmSecretSearch *self, GkmObject *object)
59 : {
60 : GkmSecretCollection *collection;
61 : GkmSecretItem *item;
62 : GHashTable *fields;
63 : const gchar *identifier;
64 : const gchar *schema;
65 :
66 2109 : if (!GKM_IS_SECRET_ITEM (object))
67 22 : return FALSE;
68 :
69 2087 : item = GKM_SECRET_ITEM (object);
70 :
71 : /* Collection should match unless any collection allowed */
72 2087 : if (self->collection_id) {
73 2064 : collection = gkm_secret_item_get_collection (item);
74 2064 : g_return_val_if_fail (collection, FALSE);
75 2064 : identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (collection));
76 2064 : g_return_val_if_fail (identifier, FALSE);
77 2064 : if (!g_str_equal (identifier, self->collection_id))
78 43 : return FALSE;
79 : }
80 :
81 2044 : fields = gkm_secret_item_get_fields (item);
82 :
83 : /* Match the schema, if we have one */
84 2044 : if (self->schema_name) {
85 12 : schema = gkm_secret_item_get_schema (item);
86 :
87 : /* Does the item has a schema set from the item type? */
88 12 : if (schema != NULL) {
89 12 : if (!g_str_equal (schema, self->schema_name))
90 8 : return FALSE;
91 :
92 : /* See if the item has a schema set in the attributes */
93 : } else {
94 0 : if (!gkm_secret_fields_match_one (fields, GKM_SECRET_FIELD_SCHEMA, self->schema_name))
95 0 : return FALSE;
96 : }
97 : }
98 :
99 : /* Fields should match using our special algorithm */
100 2036 : return gkm_secret_fields_match (fields, self->fields);
101 : }
102 :
103 : static void
104 2107 : on_manager_added_object (GkmManager *manager, GkmObject *object, gpointer user_data)
105 : {
106 2107 : GkmSecretSearch *self = user_data;
107 :
108 2107 : g_return_if_fail (GKM_IS_SECRET_SEARCH (self));
109 :
110 2107 : g_return_if_fail (g_hash_table_lookup (self->objects, object) == NULL);
111 :
112 2107 : if (match_object_against_criteria (self, object)) {
113 2011 : g_hash_table_replace (self->objects, g_object_ref (object), "unused");
114 2011 : gkm_object_notify_attribute (GKM_OBJECT (self), CKA_G_MATCHED);
115 : }
116 : }
117 :
118 : static void
119 148 : on_manager_removed_object (GkmManager *manager, GkmObject *object, gpointer user_data)
120 : {
121 148 : GkmSecretSearch *self = user_data;
122 :
123 148 : g_return_if_fail (GKM_IS_SECRET_SEARCH (self));
124 :
125 148 : if (g_hash_table_remove (self->objects, object))
126 0 : gkm_object_notify_attribute (GKM_OBJECT (self), CKA_G_MATCHED);
127 : }
128 :
129 : static void
130 4 : on_manager_changed_object (GkmManager *manager, GkmObject *object,
131 : CK_ATTRIBUTE_TYPE type, gpointer user_data)
132 : {
133 4 : GkmSecretSearch *self = user_data;
134 : CK_OBJECT_HANDLE handle;
135 :
136 4 : if (type != CKA_G_FIELDS)
137 2 : return;
138 :
139 2 : g_return_if_fail (GKM_IS_SECRET_SEARCH (self));
140 :
141 2 : handle = gkm_object_get_handle (object);
142 2 : g_return_if_fail (handle);
143 :
144 : /* Should we have this object? */
145 2 : if (match_object_against_criteria (self, object)) {
146 1 : if (g_hash_table_lookup (self->objects, object) == NULL) {
147 1 : g_hash_table_replace (self->objects, g_object_ref (object), "unused");
148 1 : gkm_object_notify_attribute (GKM_OBJECT (self), CKA_G_MATCHED);
149 : }
150 :
151 : /* Should we not have this object? */
152 : } else {
153 1 : if (g_hash_table_remove (self->objects, object))
154 1 : gkm_object_notify_attribute (GKM_OBJECT (self), CKA_G_MATCHED);
155 : }
156 : }
157 :
158 : static void
159 0 : on_manager_gone_away (gpointer user_data, GObject *where_the_object_was)
160 : {
161 0 : GkmSecretSearch *self = GKM_SECRET_SEARCH (user_data);
162 : GList *l;
163 :
164 0 : g_return_if_fail (self);
165 :
166 0 : l = g_list_find (self->managers, where_the_object_was);
167 0 : g_return_if_fail (l != NULL);
168 0 : self->managers = g_list_delete_link (self->managers, l);
169 : }
170 :
171 : static void
172 40 : populate_search_from_manager (GkmSecretSearch *self, GkmSession *session, GkmManager *manager)
173 : {
174 : GList *objects, *o;
175 :
176 40 : self->managers = g_list_append (self->managers, manager);
177 :
178 : /* Add in all the objects */
179 40 : objects = gkm_manager_find_by_class (manager, session, CKO_SECRET_KEY);
180 2127 : for (o = objects; o; o = g_list_next (o))
181 2087 : on_manager_added_object (manager, o->data, self);
182 40 : g_list_free (objects);
183 :
184 : /* Track this manager */
185 40 : g_object_weak_ref (G_OBJECT (manager), on_manager_gone_away, self);
186 :
187 : /* Watch for further events of objects */
188 40 : g_signal_connect (manager, "object-added", G_CALLBACK (on_manager_added_object), self);
189 40 : g_signal_connect (manager, "object-removed", G_CALLBACK (on_manager_removed_object), self);
190 40 : g_signal_connect (manager, "attribute-changed", G_CALLBACK (on_manager_changed_object), self);
191 40 : }
192 :
193 : static GkmObject*
194 22 : factory_create_search (GkmSession *session, GkmTransaction *transaction,
195 : CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs)
196 : {
197 : GkmManager *s_manager, *m_manager;
198 : GkmSecretSearch *search;
199 22 : gchar *identifier = NULL;
200 : CK_ATTRIBUTE *attr;
201 : GHashTable *fields;
202 : gchar *schema_name;
203 : GkmModule *module;
204 : CK_RV rv;
205 :
206 22 : g_return_val_if_fail (GKM_IS_TRANSACTION (transaction), NULL);
207 22 : g_return_val_if_fail (attrs || !n_attrs, NULL);
208 :
209 : /* Find the fields being requested */
210 22 : attr = gkm_attributes_find (attrs, n_attrs, CKA_G_FIELDS);
211 22 : if (attr == NULL) {
212 1 : gkm_transaction_fail (transaction, CKR_TEMPLATE_INCOMPLETE);
213 1 : return NULL;
214 : }
215 :
216 : /* Parse the fields, into our internal representation */
217 21 : rv = gkm_secret_fields_parse (attr, &fields, &schema_name);
218 21 : gkm_attribute_consume (attr);
219 21 : if (rv != CKR_OK) {
220 1 : gkm_transaction_fail (transaction, rv);
221 1 : return NULL;
222 : }
223 :
224 : /* Remove the schema name from the search fields, handle that separately */
225 20 : g_hash_table_remove (fields, GKM_SECRET_FIELD_SCHEMA);
226 :
227 20 : s_manager = gkm_session_get_manager (session);
228 20 : module = gkm_session_get_module (session);
229 20 : m_manager = gkm_module_get_manager (module);
230 :
231 : /* See if a collection attribute was specified, not present means all collections */
232 20 : attr = gkm_attributes_find (attrs, n_attrs, CKA_G_COLLECTION);
233 20 : if (attr) {
234 15 : rv = gkm_attribute_get_string (attr, &identifier);
235 15 : if (rv != CKR_OK) {
236 0 : g_free (schema_name);
237 0 : g_hash_table_unref (fields);
238 0 : gkm_transaction_fail (transaction, rv);
239 0 : return NULL;
240 : }
241 : }
242 :
243 20 : search = g_object_new (GKM_TYPE_SECRET_SEARCH,
244 : "module", module,
245 : "manager", s_manager,
246 : "fields", fields,
247 : "schema-name", schema_name,
248 : "collection-id", identifier,
249 : NULL);
250 20 : g_free (identifier);
251 :
252 : /* Load any new items or collections */
253 20 : gkm_module_refresh_token (module);
254 :
255 20 : populate_search_from_manager (search, session, s_manager);
256 20 : populate_search_from_manager (search, session, m_manager);
257 :
258 20 : gkm_session_complete_object_creation (session, transaction, GKM_OBJECT (search),
259 : TRUE, attrs, n_attrs);
260 :
261 20 : g_hash_table_unref (fields);
262 20 : g_free (schema_name);
263 :
264 20 : return GKM_OBJECT (search);
265 : }
266 :
267 : static gint
268 19427 : on_matched_sort_modified (gconstpointer a,
269 : gconstpointer b)
270 : {
271 : glong modified_a;
272 : glong modified_b;
273 :
274 : /* Sorting in reverse order */
275 :
276 19427 : modified_a = gkm_secret_object_get_modified (GKM_SECRET_OBJECT (a));
277 19427 : modified_b = gkm_secret_object_get_modified (GKM_SECRET_OBJECT (b));
278 :
279 19427 : if (modified_a < modified_b)
280 9806 : return 1;
281 9621 : if (modified_a > modified_b)
282 9621 : return -1;
283 :
284 0 : return 0;
285 : }
286 :
287 : static CK_RV
288 42 : attribute_set_handles (GHashTable *objects,
289 : CK_ATTRIBUTE_PTR attr)
290 : {
291 : GList *list, *l;
292 : GArray *array;
293 : gulong handle;
294 : CK_RV rv;
295 :
296 42 : g_assert (objects);
297 42 : g_assert (attr);
298 :
299 : /* Want the length */
300 42 : if (!attr->pValue) {
301 21 : attr->ulValueLen = sizeof (CK_OBJECT_HANDLE) * g_hash_table_size (objects);
302 21 : return CKR_OK;
303 : }
304 :
305 : /* Get the actual values */
306 21 : list = g_list_sort (g_hash_table_get_keys (objects), on_matched_sort_modified);
307 21 : array = g_array_new (FALSE, TRUE, sizeof (CK_OBJECT_HANDLE));
308 :
309 2033 : for (l = list; l != NULL; l = g_list_next (l)) {
310 2012 : handle = gkm_object_get_handle (l->data);
311 2012 : g_array_append_val (array, handle);
312 : }
313 :
314 21 : rv = gkm_attribute_set_data (attr, array->data, array->len * sizeof (CK_OBJECT_HANDLE));
315 21 : g_array_free (array, TRUE);
316 21 : g_list_free (list);
317 :
318 21 : return rv;
319 : }
320 :
321 : /* -----------------------------------------------------------------------------
322 : * OBJECT
323 : */
324 :
325 : static CK_RV
326 131 : gkm_secret_search_get_attribute (GkmObject *base, GkmSession *session, CK_ATTRIBUTE_PTR attr)
327 : {
328 131 : GkmSecretSearch *self = GKM_SECRET_SEARCH (base);
329 :
330 131 : switch (attr->type) {
331 47 : case CKA_CLASS:
332 47 : return gkm_attribute_set_ulong (attr, CKO_G_SEARCH);
333 1 : case CKA_MODIFIABLE:
334 1 : return gkm_attribute_set_bool (attr, CK_TRUE); /* TODO: This is needed for deleting? */
335 19 : case CKA_G_COLLECTION:
336 19 : if (!self->collection_id)
337 2 : return gkm_attribute_set_empty (attr);
338 17 : return gkm_attribute_set_string (attr, self->collection_id);
339 2 : case CKA_G_FIELDS:
340 2 : return gkm_secret_fields_serialize (attr, self->fields, self->schema_name);
341 42 : case CKA_G_MATCHED:
342 42 : return attribute_set_handles (self->objects, attr);
343 : }
344 :
345 20 : return GKM_OBJECT_CLASS (gkm_secret_search_parent_class)->get_attribute (base, session, attr);
346 : }
347 :
348 :
349 : static void
350 20 : gkm_secret_search_init (GkmSecretSearch *self)
351 : {
352 20 : self->objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
353 20 : }
354 :
355 : static GObject*
356 20 : gkm_secret_search_constructor (GType type, guint n_props, GObjectConstructParam *props)
357 : {
358 20 : GkmSecretSearch *self = GKM_SECRET_SEARCH (G_OBJECT_CLASS (gkm_secret_search_parent_class)->constructor(type, n_props, props));
359 20 : g_return_val_if_fail (self, NULL);
360 :
361 20 : g_return_val_if_fail (self->fields, NULL);
362 :
363 20 : return G_OBJECT (self);
364 : }
365 :
366 : static void
367 60 : gkm_secret_search_set_property (GObject *obj, guint prop_id, const GValue *value,
368 : GParamSpec *pspec)
369 : {
370 60 : GkmSecretSearch *self = GKM_SECRET_SEARCH (obj);
371 60 : switch (prop_id) {
372 20 : case PROP_COLLECTION_ID:
373 20 : g_return_if_fail (!self->collection_id);
374 20 : self->collection_id = g_value_dup_string (value);
375 20 : break;
376 20 : case PROP_FIELDS:
377 20 : g_return_if_fail (!self->fields);
378 20 : self->fields = g_value_dup_boxed (value);
379 20 : g_return_if_fail (self->fields);
380 20 : break;
381 20 : case PROP_SCHEMA_NAME:
382 20 : g_return_if_fail (self->schema_name == NULL);
383 20 : self->schema_name = g_value_dup_string (value);
384 20 : break;
385 0 : default:
386 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
387 0 : break;
388 : }
389 : }
390 :
391 : static void
392 0 : gkm_secret_search_get_property (GObject *obj, guint prop_id, GValue *value,
393 : GParamSpec *pspec)
394 : {
395 0 : GkmSecretSearch *self = GKM_SECRET_SEARCH (obj);
396 0 : switch (prop_id) {
397 0 : case PROP_COLLECTION_ID:
398 0 : g_value_set_string (value, self->collection_id);
399 0 : break;
400 0 : case PROP_FIELDS:
401 0 : g_return_if_fail (self->fields);
402 0 : g_value_set_boxed (value, gkm_secret_search_get_fields (self));
403 0 : break;
404 0 : case PROP_SCHEMA_NAME:
405 0 : g_value_set_string (value, self->schema_name);
406 0 : break;
407 0 : default:
408 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
409 0 : break;
410 : }
411 : }
412 :
413 : static void
414 40 : gkm_secret_search_dispose (GObject *obj)
415 : {
416 40 : GkmSecretSearch *self = GKM_SECRET_SEARCH (obj);
417 : GList *l;
418 :
419 80 : for (l = self->managers; l; l = g_list_next (l)) {
420 40 : g_signal_handlers_disconnect_by_func (l->data, on_manager_added_object, self);
421 40 : g_signal_handlers_disconnect_by_func (l->data, on_manager_removed_object, self);
422 40 : g_signal_handlers_disconnect_by_func (l->data, on_manager_changed_object, self);
423 40 : g_object_weak_unref (G_OBJECT (l->data), on_manager_gone_away, self);
424 : }
425 40 : g_list_free (self->managers);
426 40 : self->managers = NULL;
427 :
428 40 : g_free (self->collection_id);
429 40 : self->collection_id = NULL;
430 :
431 40 : g_hash_table_remove_all (self->objects);
432 :
433 40 : G_OBJECT_CLASS (gkm_secret_search_parent_class)->dispose (obj);
434 40 : }
435 :
436 : static void
437 20 : gkm_secret_search_finalize (GObject *obj)
438 : {
439 20 : GkmSecretSearch *self = GKM_SECRET_SEARCH (obj);
440 :
441 20 : g_assert (!self->managers);
442 :
443 20 : g_free (self->schema_name);
444 20 : self->schema_name = NULL;
445 :
446 20 : if (self->fields)
447 20 : g_hash_table_destroy (self->fields);
448 20 : self->fields = NULL;
449 :
450 20 : g_hash_table_destroy (self->objects);
451 :
452 20 : G_OBJECT_CLASS (gkm_secret_search_parent_class)->finalize (obj);
453 20 : }
454 :
455 : static void
456 6 : gkm_secret_search_class_init (GkmSecretSearchClass *klass)
457 : {
458 6 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
459 6 : GkmObjectClass *gkm_class = GKM_OBJECT_CLASS (klass);
460 :
461 6 : gkm_secret_search_parent_class = g_type_class_peek_parent (klass);
462 :
463 6 : gobject_class->constructor = gkm_secret_search_constructor;
464 6 : gobject_class->dispose = gkm_secret_search_dispose;
465 6 : gobject_class->finalize = gkm_secret_search_finalize;
466 6 : gobject_class->set_property = gkm_secret_search_set_property;
467 6 : gobject_class->get_property = gkm_secret_search_get_property;
468 :
469 6 : gkm_class->get_attribute = gkm_secret_search_get_attribute;
470 :
471 6 : g_object_class_install_property (gobject_class, PROP_COLLECTION_ID,
472 : g_param_spec_string ("collection-id", "Collection ID", "Item's Collection's Identifier",
473 : NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
474 :
475 6 : g_object_class_install_property (gobject_class, PROP_FIELDS,
476 : g_param_spec_boxed ("fields", "Fields", "Item's fields",
477 : GKM_BOXED_SECRET_FIELDS, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
478 :
479 6 : g_object_class_install_property (gobject_class, PROP_SCHEMA_NAME,
480 : g_param_spec_string ("schema_name", "Schema Name", "Schema name to match",
481 : NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
482 6 : }
483 :
484 : /* -----------------------------------------------------------------------------
485 : * PUBLIC
486 : */
487 :
488 : GkmFactory*
489 125 : gkm_secret_search_get_factory (void)
490 : {
491 : static CK_OBJECT_CLASS klass = CKO_G_SEARCH;
492 : static CK_BBOOL token = CK_FALSE;
493 :
494 : static CK_ATTRIBUTE attributes[] = {
495 : { CKA_CLASS, &klass, sizeof (klass) },
496 : { CKA_TOKEN, &token, sizeof (token) },
497 : };
498 :
499 : static GkmFactory factory = {
500 : attributes,
501 : G_N_ELEMENTS (attributes),
502 : factory_create_search
503 : };
504 :
505 125 : return &factory;
506 : }
507 :
508 : GHashTable*
509 1 : gkm_secret_search_get_fields (GkmSecretSearch *self)
510 : {
511 1 : g_return_val_if_fail (GKM_IS_SECRET_SEARCH (self), NULL);
512 1 : return self->fields;
513 : }
514 :
515 : const gchar *
516 0 : gkm_secret_search_get_schema_name (GkmSecretSearch *self)
517 : {
518 0 : g_return_val_if_fail (GKM_IS_SECRET_SEARCH (self), NULL);
519 0 : return self->schema_name;
520 : }
521 :
522 : const gchar*
523 1 : gkm_secret_search_get_collection_id (GkmSecretSearch *self)
524 : {
525 1 : g_return_val_if_fail (GKM_IS_SECRET_SEARCH (self), NULL);
526 1 : return self->collection_id;
527 : }
|