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-item.h"
25 : #include "gkm-secret-module.h"
26 : #include "gkm-secret-search.h"
27 : #include "gkm-secret-store.h"
28 :
29 : #include "gkm/gkm-credential.h"
30 : #define DEBUG_FLAG GKM_DEBUG_STORAGE
31 : #include "gkm/gkm-debug.h"
32 : #include "gkm/gkm-transaction.h"
33 : #include "gkm/gkm-util.h"
34 :
35 : #include "egg/egg-file-tracker.h"
36 :
37 : #include <glib/gstdio.h>
38 :
39 : #include <errno.h>
40 : #include <fcntl.h>
41 : #include <string.h>
42 :
43 : struct _GkmSecretModule {
44 : GkmModule parent;
45 : EggFileTracker *tracker;
46 : GHashTable *collections;
47 : gchar *directory;
48 : GkmCredential *session_credential;
49 : };
50 :
51 : static const CK_SLOT_INFO gkm_secret_module_slot_info = {
52 : "Secret Store",
53 : "Gnome Keyring",
54 : CKF_TOKEN_PRESENT,
55 : { 0, 0 },
56 : { 0, 0 }
57 : };
58 :
59 : static const CK_TOKEN_INFO gkm_secret_module_token_info = {
60 : "Secret Store",
61 : "Gnome Keyring",
62 : "1.0",
63 : "1:SECRET:MAIN", /* Unique serial number for manufacturer */
64 : CKF_TOKEN_INITIALIZED | CKF_USER_PIN_INITIALIZED | CKF_LOGIN_REQUIRED,
65 : CK_EFFECTIVELY_INFINITE,
66 : CK_EFFECTIVELY_INFINITE,
67 : CK_EFFECTIVELY_INFINITE,
68 : CK_EFFECTIVELY_INFINITE,
69 : 1024,
70 : 1,
71 : CK_UNAVAILABLE_INFORMATION,
72 : CK_UNAVAILABLE_INFORMATION,
73 : CK_UNAVAILABLE_INFORMATION,
74 : CK_UNAVAILABLE_INFORMATION,
75 : { 0, 0 },
76 : { 0, 0 },
77 : ""
78 : };
79 :
80 1048 : G_DEFINE_TYPE (GkmSecretModule, gkm_secret_module, GKM_TYPE_MODULE);
81 :
82 : GkmModule* _gkm_secret_store_get_module_for_testing (void);
83 :
84 : /* Forward declarations */
85 : static void add_collection (GkmSecretModule *, GkmTransaction *, GkmSecretCollection *);
86 : static void remove_collection (GkmSecretModule *, GkmTransaction *, GkmSecretCollection *);
87 :
88 : /* -----------------------------------------------------------------------------
89 : * ACTUAL PKCS#11 Module Implementation
90 : */
91 :
92 : /* Include all the module entry points */
93 : #include "gkm/gkm-module-ep.h"
94 109 : GKM_DEFINE_MODULE (gkm_secret_module, GKM_TYPE_SECRET_MODULE);
95 :
96 : /* -----------------------------------------------------------------------------
97 : * INTERNAL
98 : */
99 :
100 : static gboolean
101 8 : complete_add (GkmTransaction *transaction, GObject *obj, gpointer user_data)
102 : {
103 8 : GkmSecretCollection *collection = GKM_SECRET_COLLECTION (user_data);
104 8 : if (gkm_transaction_get_failed (transaction))
105 0 : remove_collection (GKM_SECRET_MODULE (obj), NULL, collection);
106 8 : g_object_unref (collection);
107 8 : return TRUE;
108 : }
109 :
110 : static void
111 67 : add_collection (GkmSecretModule *self, GkmTransaction *transaction, GkmSecretCollection *collection)
112 : {
113 : const gchar *filename;
114 :
115 67 : g_assert (GKM_IS_SECRET_MODULE(self));
116 67 : g_assert (GKM_IS_SECRET_COLLECTION (collection));
117 :
118 67 : filename = gkm_secret_collection_get_filename (collection);
119 67 : g_return_if_fail (filename);
120 :
121 134 : g_hash_table_replace (self->collections, g_strdup (filename), g_object_ref (collection));
122 :
123 67 : gkm_object_expose_full (GKM_OBJECT (collection), transaction, TRUE);
124 67 : if (transaction)
125 8 : gkm_transaction_add (transaction, self, complete_add, g_object_ref (collection));
126 : }
127 :
128 : static gboolean
129 2 : complete_remove (GkmTransaction *transaction, GObject *obj, gpointer user_data)
130 : {
131 2 : GkmSecretCollection *collection = GKM_SECRET_COLLECTION (user_data);
132 2 : if (gkm_transaction_get_failed (transaction))
133 0 : add_collection (GKM_SECRET_MODULE (obj), NULL, collection);
134 2 : g_object_unref (collection);
135 2 : return TRUE;
136 : }
137 :
138 : static void
139 2 : remove_collection (GkmSecretModule *self, GkmTransaction *transaction, GkmSecretCollection *collection)
140 : {
141 : const gchar *filename;
142 :
143 2 : g_assert (GKM_IS_SECRET_MODULE (self));
144 2 : g_assert (GKM_IS_SECRET_COLLECTION (collection));
145 :
146 2 : filename = gkm_secret_collection_get_filename (collection);
147 2 : g_return_if_fail (filename);
148 :
149 2 : g_hash_table_remove (self->collections, filename);
150 :
151 2 : gkm_object_expose_full (GKM_OBJECT (collection), transaction, FALSE);
152 2 : if (transaction)
153 2 : gkm_transaction_add (transaction, self, complete_remove, g_object_ref (collection));
154 : }
155 :
156 : static gchar*
157 59 : identifier_from_filename (GkmSecretModule *self, const gchar *filename)
158 : {
159 : gchar *identifier;
160 :
161 : /* Do we have one for this path yet? */
162 59 : identifier = g_path_get_basename (filename);
163 :
164 : /* Remove the keyring suffix */
165 59 : if (g_str_has_suffix (identifier, ".keyring"))
166 59 : identifier[strlen(identifier) - 8] = 0;
167 :
168 59 : return identifier;
169 : }
170 :
171 : static gchar*
172 8 : identifier_to_new_filename (GkmSecretModule *self, const gchar *identifier)
173 : {
174 : gchar *filename;
175 : gint i;
176 : int fd;
177 :
178 8 : for (i = 0; i < G_MAXINT; ++i) {
179 8 : if (i == 0)
180 8 : filename = g_strdup_printf ("%s/%s.keyring", self->directory, identifier);
181 : else
182 0 : filename = g_strdup_printf ("%s/%s_%d.keyring", self->directory, identifier, i);
183 :
184 : /* Try to create the file, and check that it doesn't exist */
185 8 : fd = g_open (filename, O_RDONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
186 8 : if (fd == -1) {
187 0 : if (errno != EEXIST)
188 0 : break;
189 : } else {
190 8 : close (fd);
191 8 : break;
192 : }
193 :
194 0 : g_free (filename);
195 : }
196 :
197 8 : return filename;
198 : }
199 :
200 :
201 : static void
202 59 : on_file_load (EggFileTracker *tracker,
203 : const gchar *path,
204 : GkmSecretModule *self)
205 : {
206 : GkmSecretCollection *collection;
207 : GkmManager *manager;
208 : GkmDataResult res;
209 : gboolean created;
210 : gchar *identifier;
211 :
212 59 : manager = gkm_module_get_manager (GKM_MODULE (self));
213 59 : g_return_if_fail (manager);
214 :
215 : /* Do we have one for this path yet? */
216 59 : identifier = identifier_from_filename (self, path);
217 59 : collection = g_hash_table_lookup (self->collections, path);
218 :
219 59 : if (collection == NULL) {
220 59 : created = TRUE;
221 59 : collection = g_object_new (GKM_TYPE_SECRET_COLLECTION,
222 : "module", self,
223 : "identifier", identifier,
224 : "filename", path,
225 : "manager", manager,
226 : NULL);
227 : } else {
228 0 : created = FALSE;
229 0 : g_object_ref (collection);
230 : }
231 :
232 59 : res = gkm_secret_collection_load (collection);
233 :
234 59 : switch (res) {
235 59 : case GKM_DATA_SUCCESS:
236 59 : if (created)
237 59 : add_collection (self, NULL, collection);
238 59 : break;
239 0 : case GKM_DATA_LOCKED:
240 0 : g_message ("master password for keyring changed without our knowledge: %s", path);
241 0 : gkm_secret_collection_unlocked_clear (collection);
242 0 : break;
243 0 : case GKM_DATA_UNRECOGNIZED:
244 0 : g_message ("keyring was in an invalid or unrecognized format: %s", path);
245 0 : break;
246 0 : case GKM_DATA_FAILURE:
247 0 : g_message ("failed to parse keyring: %s", path);
248 0 : break;
249 0 : default:
250 0 : g_assert_not_reached ();
251 : }
252 :
253 59 : g_object_unref (collection);
254 59 : g_free (identifier);
255 : }
256 :
257 : static void
258 1 : on_file_remove (EggFileTracker *tracker,
259 : const gchar *path,
260 : GkmSecretModule *self)
261 : {
262 : GkmSecretCollection *collection;
263 :
264 1 : g_return_if_fail (path);
265 1 : g_return_if_fail (GKM_IS_SECRET_MODULE (self));
266 :
267 1 : collection = g_hash_table_lookup (self->collections, path);
268 1 : if (collection)
269 0 : remove_collection (self, NULL, collection);
270 : }
271 :
272 : /* -----------------------------------------------------------------------------
273 : * OBJECT
274 : */
275 :
276 : static const CK_SLOT_INFO*
277 66 : gkm_secret_module_real_get_slot_info (GkmModule *self)
278 : {
279 66 : return &gkm_secret_module_slot_info;
280 : }
281 :
282 : static const CK_TOKEN_INFO*
283 92 : gkm_secret_module_real_get_token_info (GkmModule *self)
284 : {
285 92 : return &gkm_secret_module_token_info;
286 : }
287 :
288 : static void
289 109 : gkm_secret_module_real_parse_argument (GkmModule *base, const gchar *name, const gchar *value)
290 : {
291 109 : GkmSecretModule *self = GKM_SECRET_MODULE (base);
292 109 : if (g_str_equal (name, "directory")) {
293 109 : g_free (self->directory);
294 109 : self->directory = g_strdup (value);
295 : }
296 109 : }
297 :
298 : static CK_RV
299 209 : gkm_secret_module_real_refresh_token (GkmModule *base)
300 : {
301 209 : GkmSecretModule *self = GKM_SECRET_MODULE (base);
302 209 : if (self->tracker)
303 209 : egg_file_tracker_refresh (self->tracker, FALSE);
304 209 : return CKR_OK;
305 : }
306 :
307 : static void
308 14 : gkm_secret_module_real_add_object (GkmModule *module, GkmTransaction *transaction,
309 : GkmObject *object)
310 : {
311 14 : GkmSecretModule *self = GKM_SECRET_MODULE (module);
312 : GkmSecretCollection *collection;
313 : const gchar *identifier;
314 : gchar *filename;
315 :
316 14 : g_return_if_fail (!gkm_transaction_get_failed (transaction));
317 :
318 14 : if (GKM_IS_SECRET_COLLECTION (object)) {
319 8 : collection = GKM_SECRET_COLLECTION (object);
320 :
321 : /* Setup a filename for this collection */
322 8 : identifier = gkm_secret_object_get_identifier (GKM_SECRET_OBJECT (collection));
323 8 : filename = identifier_to_new_filename (self, identifier);
324 8 : gkm_secret_collection_set_filename (collection, filename);
325 8 : g_free (filename);
326 :
327 8 : add_collection (self, transaction, collection);
328 : }
329 : }
330 :
331 : static void
332 29 : gkm_secret_module_real_store_object (GkmModule *module, GkmTransaction *transaction,
333 : GkmObject *object)
334 : {
335 29 : GkmSecretModule *self = GKM_SECRET_MODULE (module);
336 29 : GkmSecretCollection *collection = NULL;
337 :
338 : /* Store the item's collection */
339 29 : if (GKM_IS_SECRET_ITEM (object)) {
340 9 : collection = gkm_secret_item_get_collection (GKM_SECRET_ITEM (object));
341 9 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (collection));
342 9 : gkm_module_store_token_object (GKM_MODULE (self), transaction, GKM_OBJECT (collection));
343 :
344 : /* Storing a collection */
345 20 : } else if (GKM_IS_SECRET_COLLECTION (object)) {
346 20 : collection = GKM_SECRET_COLLECTION (object);
347 20 : gkm_secret_collection_save (collection, transaction);
348 :
349 : /* No other kind of token object */
350 : } else {
351 0 : g_warning ("can't store object of type '%s' on secret token", G_OBJECT_TYPE_NAME (object));
352 0 : gkm_transaction_fail (transaction, CKR_GENERAL_ERROR);
353 : }
354 : }
355 :
356 : static void
357 4 : gkm_secret_module_real_remove_object (GkmModule *module, GkmTransaction *transaction,
358 : GkmObject *object)
359 : {
360 4 : GkmSecretModule *self = GKM_SECRET_MODULE (module);
361 : GkmSecretCollection *collection;
362 :
363 : /* Ignore the session keyring credentials */
364 8 : if (self->session_credential != NULL &&
365 4 : GKM_OBJECT (self->session_credential) == object)
366 0 : return;
367 :
368 : /* Removing an item */
369 4 : if (GKM_IS_SECRET_ITEM (object)) {
370 2 : collection = gkm_secret_item_get_collection (GKM_SECRET_ITEM (object));
371 2 : g_return_if_fail (GKM_IS_SECRET_COLLECTION (collection));
372 2 : gkm_secret_collection_destroy_item (collection, transaction, GKM_SECRET_ITEM (object));
373 2 : if (!gkm_transaction_get_failed (transaction))
374 2 : gkm_secret_collection_save (collection, transaction);
375 :
376 : /* Removing a collection */
377 2 : } else if (GKM_IS_SECRET_COLLECTION (object)) {
378 2 : collection = GKM_SECRET_COLLECTION (object);
379 2 : gkm_secret_collection_destroy (collection, transaction);
380 2 : if (!gkm_transaction_get_failed (transaction))
381 2 : remove_collection (self, transaction, collection);
382 :
383 : /* No other token objects */
384 : } else {
385 0 : g_warning ("Trying to remove token object of type '%s' from secret "
386 : "module, but that type is not supported.", G_OBJECT_TYPE_NAME (object));
387 0 : gkm_transaction_fail (transaction, CKR_FUNCTION_NOT_SUPPORTED);
388 : }
389 : }
390 :
391 : static GObject*
392 109 : gkm_secret_module_constructor (GType type, guint n_props, GObjectConstructParam *props)
393 : {
394 109 : GkmSecretModule *self = GKM_SECRET_MODULE (G_OBJECT_CLASS (gkm_secret_module_parent_class)->constructor(type, n_props, props));
395 : GkmManager *manager;
396 : GkmObject *collection;
397 : CK_RV rv;
398 :
399 109 : g_return_val_if_fail (self, NULL);
400 :
401 109 : if (!self->directory)
402 0 : self->directory = gkm_util_locate_keyrings_directory ();
403 109 : gkm_debug ("secret store directory: %s", self->directory);
404 :
405 109 : self->tracker = egg_file_tracker_new (self->directory, "*.keyring", NULL);
406 109 : g_signal_connect (self->tracker, "file-added", G_CALLBACK (on_file_load), self);
407 109 : g_signal_connect (self->tracker, "file-changed", G_CALLBACK (on_file_load), self);
408 109 : g_signal_connect (self->tracker, "file-removed", G_CALLBACK (on_file_remove), self);
409 :
410 109 : manager = gkm_module_get_manager (GKM_MODULE (self));
411 :
412 109 : collection = g_object_new (GKM_TYPE_SECRET_COLLECTION,
413 : "module", self,
414 : "identifier", "session",
415 : "manager", manager,
416 : "transient", TRUE,
417 : NULL);
418 :
419 : /* Create the 'session' keyring, which is not stored to disk */
420 109 : g_return_val_if_fail (gkm_object_is_transient (collection), NULL);
421 109 : gkm_module_add_token_object (GKM_MODULE (self), NULL, collection);
422 109 : gkm_object_expose (collection, TRUE);
423 :
424 : /* Unlock the 'session' keyring */
425 109 : rv = gkm_credential_create (GKM_MODULE (self), manager, GKM_OBJECT (collection),
426 : NULL, 0, &self->session_credential);
427 109 : if (rv == CKR_OK)
428 109 : gkm_object_expose (GKM_OBJECT (self->session_credential), TRUE);
429 : else
430 0 : g_warning ("couldn't unlock the 'session' keyring");
431 :
432 109 : g_object_unref (collection);
433 109 : return G_OBJECT (self);
434 : }
435 :
436 : static void
437 109 : gkm_secret_module_init (GkmSecretModule *self)
438 : {
439 109 : self->collections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
440 109 : gkm_module_register_factory (GKM_MODULE (self), GKM_FACTORY_SECRET_SEARCH);
441 109 : gkm_module_register_factory (GKM_MODULE (self), GKM_FACTORY_SECRET_ITEM);
442 109 : gkm_module_register_factory (GKM_MODULE (self), GKM_FACTORY_SECRET_COLLECTION);
443 109 : }
444 :
445 : static void
446 218 : gkm_secret_module_dispose (GObject *obj)
447 : {
448 218 : GkmSecretModule *self = GKM_SECRET_MODULE (obj);
449 :
450 218 : if (self->tracker)
451 109 : g_object_unref (self->tracker);
452 218 : self->tracker = NULL;
453 :
454 218 : if (self->session_credential)
455 109 : g_object_unref (self->session_credential);
456 218 : self->session_credential = NULL;
457 :
458 218 : g_hash_table_remove_all (self->collections);
459 :
460 218 : G_OBJECT_CLASS (gkm_secret_module_parent_class)->dispose (obj);
461 218 : }
462 :
463 : static void
464 109 : gkm_secret_module_finalize (GObject *obj)
465 : {
466 109 : GkmSecretModule *self = GKM_SECRET_MODULE (obj);
467 :
468 109 : g_assert (self->tracker == NULL);
469 :
470 109 : g_hash_table_destroy (self->collections);
471 109 : self->collections = NULL;
472 :
473 109 : g_free (self->directory);
474 109 : self->directory = NULL;
475 :
476 109 : g_assert (!self->session_credential);
477 :
478 109 : G_OBJECT_CLASS (gkm_secret_module_parent_class)->finalize (obj);
479 109 : }
480 :
481 : static void
482 34 : gkm_secret_module_class_init (GkmSecretModuleClass *klass)
483 : {
484 34 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
485 34 : GkmModuleClass *module_class = GKM_MODULE_CLASS (klass);
486 :
487 34 : gobject_class->constructor = gkm_secret_module_constructor;
488 34 : gobject_class->dispose = gkm_secret_module_dispose;
489 34 : gobject_class->finalize = gkm_secret_module_finalize;
490 :
491 34 : module_class->get_slot_info = gkm_secret_module_real_get_slot_info;
492 34 : module_class->get_token_info = gkm_secret_module_real_get_token_info;
493 34 : module_class->parse_argument = gkm_secret_module_real_parse_argument;
494 34 : module_class->refresh_token = gkm_secret_module_real_refresh_token;
495 34 : module_class->add_token_object = gkm_secret_module_real_add_object;
496 34 : module_class->store_token_object = gkm_secret_module_real_store_object;
497 34 : module_class->remove_token_object = gkm_secret_module_real_remove_object;
498 34 : }
499 :
500 : /* ---------------------------------------------------------------------------------------
501 : * PUBLIC
502 : */
503 :
504 : CK_FUNCTION_LIST_PTR
505 191 : gkm_secret_store_get_functions (void)
506 : {
507 191 : gkm_crypto_initialize ();
508 191 : return gkm_secret_module_function_list;
509 : }
510 :
511 : GkmModule*
512 164 : _gkm_secret_store_get_module_for_testing (void)
513 : {
514 164 : return pkcs11_module;
515 : }
|