Branch data Line data Source code
1 : : /*
2 : : * gnome-keyring
3 : : *
4 : : * Copyright (C) 2008 Stefan Walter
5 : : * Copyright (C) 2011 Collabora Ltd.
6 : : *
7 : : * This program is free software; you can redistribute it and/or modify
8 : : * it under the terms of the GNU Lesser General Public License as
9 : : * published by the Free Software Foundation; either version 2.1 of
10 : : * the License, or (at your option) any later version.
11 : : *
12 : : * This program is distributed in the hope that it will be useful, but
13 : : * WITHOUT ANY WARRANTY; without even the implied warranty of
14 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 : : * Lesser General Public License for more details.
16 : : *
17 : : * You should have received a copy of the GNU Lesser General Public
18 : : * License along with this program; if not, see <http://www.gnu.org/licenses/>.
19 : : *
20 : : * Author: Stef Walter <stefw@collabora.co.uk>
21 : : */
22 : :
23 : : #include "config.h"
24 : :
25 : : #include "gcr-fingerprint.h"
26 : : #include "gcr-internal.h"
27 : : #include "gcr-library.h"
28 : : #include "gcr-import-interaction.h"
29 : : #include "gcr-internal.h"
30 : : #include "gcr-parser.h"
31 : : #include "gcr-pkcs11-importer.h"
32 : :
33 : : #include "egg/egg-hex.h"
34 : :
35 : : #include <gck/gck.h>
36 : :
37 : : #include <gcrypt.h>
38 : :
39 : : #include <glib/gi18n-lib.h>
40 : :
41 : : enum {
42 : : PROP_0,
43 : : PROP_LABEL,
44 : : PROP_INTERACTION,
45 : : PROP_SLOT,
46 : : PROP_QUEUED,
47 : : PROP_URI
48 : : };
49 : :
50 : : struct _GcrPkcs11Importer {
51 : : GObject parent;
52 : : GckSlot *slot;
53 : : GList *objects;
54 : : GckSession *session;
55 : : GQueue *queue;
56 : : GTlsInteraction *interaction;
57 : : gboolean any_private;
58 : : };
59 : :
60 : : typedef struct {
61 : : GcrPkcs11Importer *importer;
62 : : gboolean prompted;
63 : : gboolean async;
64 : : GckBuilder *supplement;
65 : : } GcrImporterData;
66 : :
67 : : /* frward declarations */
68 : : static void state_cancelled (GTask *task,
69 : : gboolean async);
70 : : static void state_create_object (GTask *task,
71 : : gboolean async);
72 : : static void _gcr_pkcs11_importer_init_iface (GcrImporterInterface *iface);
73 : :
74 [ # # # # : 0 : G_DEFINE_TYPE_WITH_CODE (GcrPkcs11Importer, _gcr_pkcs11_importer, G_TYPE_OBJECT,
# # ]
75 : : G_IMPLEMENT_INTERFACE (GCR_TYPE_IMPORTER, _gcr_pkcs11_importer_init_iface);
76 : : );
77 : :
78 : : #define BLOCK 4096
79 : :
80 : : static void
81 : 0 : gcr_importer_data_free (gpointer data)
82 : : {
83 : 0 : GcrImporterData *state = data;
84 : :
85 [ # # ]: 0 : g_clear_object (&state->importer);
86 : 0 : gck_builder_unref (state->supplement);
87 : 0 : g_free (state);
88 : 0 : }
89 : :
90 : : static void
91 : 0 : next_state (GTask *task,
92 : : void (*state) (GTask *, gboolean))
93 : : {
94 : 0 : GcrImporterData *data = g_task_get_task_data (task);
95 : :
96 [ # # ]: 0 : g_assert (state);
97 : :
98 [ # # ]: 0 : if (g_cancellable_is_cancelled (g_task_get_cancellable (task)))
99 : 0 : state = state_cancelled;
100 : :
101 : 0 : (state) (task, data->async);
102 : 0 : }
103 : :
104 : : /* ---------------------------------------------------------------------------------
105 : : * COMPLETE
106 : : */
107 : :
108 : : static void
109 : 0 : state_cancelled (GTask *task,
110 : : gboolean async)
111 : : {
112 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
113 : 0 : GError *error = NULL;
114 : :
115 [ # # # # ]: 0 : if (cancellable && !g_cancellable_is_cancelled (cancellable))
116 : 0 : g_cancellable_cancel (cancellable);
117 : :
118 : 0 : g_cancellable_set_error_if_cancelled (cancellable, &error);
119 : 0 : g_task_return_error (task, g_steal_pointer (&error));
120 : 0 : }
121 : :
122 : : /* ---------------------------------------------------------------------------------
123 : : * CREATE OBJECTS
124 : : */
125 : :
126 : : static void
127 : 0 : complete_create_object (GTask *task,
128 : : GckObject *object,
129 : : GError *error)
130 : : {
131 : 0 : GcrImporterData *data = g_task_get_task_data (task);
132 : 0 : GcrPkcs11Importer *self = data->importer;
133 : :
134 [ # # ]: 0 : if (object == NULL) {
135 : 0 : g_task_return_error (task, g_steal_pointer (&error));
136 : :
137 : : } else {
138 : 0 : self->objects = g_list_append (self->objects, object);
139 : 0 : next_state (task, state_create_object);
140 : : }
141 : 0 : }
142 : :
143 : : static void
144 : 0 : on_create_object (GObject *source,
145 : : GAsyncResult *result,
146 : : gpointer user_data)
147 : : {
148 : 0 : GTask *task = G_TASK (user_data);
149 : 0 : GError *error = NULL;
150 : : GckObject *object;
151 : :
152 : 0 : object = gck_session_create_object_finish (GCK_SESSION (source), result, &error);
153 : 0 : complete_create_object (task, object, error);
154 [ # # ]: 0 : g_clear_object (&task);
155 : 0 : }
156 : :
157 : : static void
158 : 0 : state_create_object (GTask *task,
159 : : gboolean async)
160 : : {
161 : 0 : GcrImporterData *data = g_task_get_task_data (task);
162 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
163 : 0 : GcrPkcs11Importer *self = data->importer;
164 : : GckAttributes *attrs;
165 : : GckObject *object;
166 : 0 : GError *error = NULL;
167 : :
168 : : /* No more objects */
169 [ # # ]: 0 : if (g_queue_is_empty (self->queue)) {
170 : 0 : g_task_return_boolean (task, TRUE);
171 : :
172 : : } else {
173 : :
174 : : /* Pop first one off the list */
175 : 0 : attrs = g_queue_pop_head (self->queue);
176 [ # # ]: 0 : g_assert (attrs != NULL);
177 : :
178 [ # # ]: 0 : if (async) {
179 : 0 : gck_session_create_object_async (self->session, attrs,
180 : : cancellable, on_create_object,
181 : : g_object_ref (task));
182 : : } else {
183 : 0 : object = gck_session_create_object (self->session, attrs,
184 : : cancellable, &error);
185 : 0 : complete_create_object (task, object, error);
186 : : }
187 : :
188 : 0 : gck_attributes_unref (attrs);
189 : : }
190 : 0 : }
191 : :
192 : : /* ---------------------------------------------------------------------------------
193 : : * SUPPLEMENTING and FIXING UP
194 : : */
195 : :
196 : : typedef struct {
197 : : GckAttributes *certificate;
198 : : GckAttributes *private_key;
199 : : } CertificateKeyPair;
200 : :
201 : : static void
202 : 0 : supplement_with_attributes (GckBuilder *builder,
203 : : GckAttributes *supplements)
204 : : {
205 : : const GckAttribute *supplement;
206 : : gint i;
207 : :
208 [ # # ]: 0 : for (i = 0; i < gck_attributes_count (supplements); i++) {
209 : 0 : supplement = gck_attributes_at (supplements, i);
210 [ # # # # ]: 0 : if (!gck_attribute_is_invalid (supplement) && supplement->length != 0)
211 : 0 : gck_builder_add_attribute (builder, supplement);
212 : : }
213 : 0 : }
214 : :
215 : : static void
216 : 0 : supplement_id_for_data (GckBuilder *builder,
217 : : guchar *nonce,
218 : : gsize n_once,
219 : : gpointer data,
220 : : gsize n_data)
221 : : {
222 : : gcry_md_hd_t mdh;
223 : : gcry_error_t gcry;
224 : :
225 [ # # ]: 0 : if (gck_builder_find (builder, CKA_ID) != NULL)
226 : 0 : return;
227 : :
228 : 0 : gcry = gcry_md_open (&mdh, GCRY_MD_SHA1, 0);
229 [ # # ]: 0 : g_return_if_fail (gcry == 0);
230 : :
231 : 0 : gcry_md_write (mdh, nonce, n_once);
232 : 0 : gcry_md_write (mdh, data, n_data);
233 : :
234 : 0 : gck_builder_add_data (builder, CKA_ID,
235 : 0 : gcry_md_read (mdh, 0),
236 : 0 : gcry_md_get_algo_dlen (GCRY_MD_SHA1));
237 : :
238 : 0 : gcry_md_close (mdh);
239 : : }
240 : :
241 : : static void
242 : 0 : supplement_attributes (GcrPkcs11Importer *self,
243 : : GckAttributes *supplements)
244 : : {
245 : 0 : GckBuilder builder = GCK_BUILDER_INIT;
246 : : GHashTable *pairs;
247 : : GHashTable *paired;
248 : : CertificateKeyPair *pair;
249 : 0 : gboolean supplemented = FALSE;
250 : : GckAttributes *attrs;
251 : : gulong klass;
252 : : guchar *finger;
253 : : gchar *fingerprint;
254 : : guchar nonce[20];
255 : : GHashTableIter iter;
256 : : gsize n_finger;
257 : : GQueue *queue;
258 : : GList *l;
259 : :
260 : : /* A table of certificate/key pairs by fingerprint */
261 : 0 : pairs = g_hash_table_new_full (g_str_hash, g_str_equal,
262 : : g_free, g_free);
263 : :
264 [ # # # # ]: 0 : for (l = self->queue->head; l != NULL; l = g_list_next (l)) {
265 : 0 : attrs = l->data;
266 [ # # ]: 0 : if (!gck_attributes_find_ulong (attrs, CKA_CLASS, &klass))
267 : 0 : g_return_if_reached ();
268 : :
269 : : /* Make a string fingerprint for this guy */
270 : 0 : finger = gcr_fingerprint_from_attributes (attrs, G_CHECKSUM_SHA1,
271 : : &n_finger);
272 [ # # ]: 0 : if (finger) {
273 : 0 : fingerprint = egg_hex_encode (finger, n_finger);
274 : 0 : g_free (finger);
275 : :
276 : 0 : pair = g_hash_table_lookup (pairs, fingerprint);
277 [ # # ]: 0 : if (pair == NULL) {
278 : 0 : pair = g_new0 (CertificateKeyPair, 1);
279 : 0 : g_hash_table_insert (pairs, fingerprint, pair);
280 : : } else {
281 : 0 : g_free (fingerprint);
282 : : }
283 : : } else {
284 : 0 : pair = NULL;
285 : : }
286 : :
287 : 0 : fingerprint = NULL;
288 : 0 : gck_builder_add_all (&builder, attrs);
289 : 0 : gck_builder_set_boolean (&builder, CKA_TOKEN, CK_TRUE);
290 : :
291 [ # # # ]: 0 : switch (klass) {
292 : 0 : case CKO_CERTIFICATE:
293 : 0 : gck_builder_set_boolean (&builder, CKA_PRIVATE, FALSE);
294 : 0 : break;
295 : 0 : case CKO_PRIVATE_KEY:
296 : 0 : gck_builder_set_boolean (&builder, CKA_PRIVATE, TRUE);
297 : 0 : gck_builder_add_boolean (&builder, CKA_DECRYPT, TRUE);
298 : 0 : gck_builder_add_boolean (&builder, CKA_SIGN, TRUE);
299 : 0 : gck_builder_add_boolean (&builder, CKA_SIGN_RECOVER, TRUE);
300 : 0 : gck_builder_add_boolean (&builder, CKA_UNWRAP, TRUE);
301 : 0 : gck_builder_add_boolean (&builder, CKA_SENSITIVE, TRUE);
302 : 0 : break;
303 : : }
304 : :
305 : 0 : gck_attributes_unref (attrs);
306 : 0 : l->data = attrs = gck_builder_end (&builder);
307 : :
308 [ # # # ]: 0 : switch (klass) {
309 : 0 : case CKO_CERTIFICATE:
310 [ # # # # ]: 0 : if (pair != NULL && pair->certificate == NULL)
311 : 0 : pair->certificate = attrs;
312 : 0 : break;
313 : 0 : case CKO_PRIVATE_KEY:
314 [ # # # # ]: 0 : if (pair != NULL && pair->private_key == NULL)
315 : 0 : pair->private_key = attrs;
316 : 0 : break;
317 : : }
318 : : }
319 : :
320 : : /* For generation of CKA_ID's */
321 : 0 : gcry_create_nonce (nonce, sizeof (nonce));
322 : :
323 : : /* A table for marking which attributes are in the pairs table */
324 : 0 : paired = g_hash_table_new (g_direct_hash, g_direct_equal);
325 : :
326 : : /* Now move everything in pairs to the front */
327 : 0 : queue = g_queue_new ();
328 : 0 : g_hash_table_iter_init (&iter, pairs);
329 [ # # ]: 0 : while (g_hash_table_iter_next (&iter, (gpointer *)&fingerprint, (gpointer *)&pair)) {
330 [ # # # # ]: 0 : if (pair->certificate != NULL && pair->private_key != NULL) {
331 : : /*
332 : : * Generate a CKA_ID based on the fingerprint and nonce,
333 : : * and do the same CKA_ID for both private key and certificate.
334 : : */
335 : :
336 : 0 : gck_builder_add_all (&builder, pair->private_key);
337 : 0 : supplement_with_attributes (&builder, supplements);
338 : 0 : supplement_id_for_data (&builder, nonce, sizeof (nonce),
339 : : fingerprint, strlen (fingerprint));
340 : 0 : g_queue_push_tail (queue, gck_builder_end (&builder));
341 : 0 : g_hash_table_insert (paired, pair->private_key, "present");
342 : :
343 : 0 : gck_builder_add_all (&builder, pair->certificate);
344 : 0 : supplement_with_attributes (&builder, supplements);
345 : 0 : supplement_id_for_data (&builder, nonce, sizeof (nonce),
346 : : fingerprint, strlen (fingerprint));
347 : 0 : g_queue_push_tail (queue, gck_builder_end (&builder));
348 : 0 : g_hash_table_insert (paired, pair->certificate, "present");
349 : :
350 : : /* Used the suplements for the pairs, don't use for unpaired stuff */
351 : 0 : supplemented = TRUE;
352 : : }
353 : : }
354 : :
355 : : /* Go through the old queue, and look for anything not paired */
356 [ # # # # ]: 0 : for (l = self->queue->head; l != NULL; l = g_list_next (l)) {
357 : 0 : attrs = l->data;
358 [ # # ]: 0 : if (!g_hash_table_lookup (paired, attrs)) {
359 : 0 : gck_builder_add_all (&builder, attrs);
360 [ # # ]: 0 : if (!supplemented)
361 : 0 : supplement_with_attributes (&builder, supplements);
362 : :
363 : : /*
364 : : * Generate a CKA_ID based on the location of attrs in,
365 : : * memory, since this together with the nonce should
366 : : * be unique.
367 : : */
368 : 0 : supplement_id_for_data (&builder, nonce, sizeof (nonce),
369 : : &attrs, sizeof (gpointer));
370 : :
371 : 0 : g_queue_push_tail (queue, gck_builder_end (&builder));
372 : : }
373 : : }
374 : :
375 : : /* And swap the new queue into place */
376 : 0 : g_queue_foreach (self->queue, (GFunc)gck_attributes_unref, NULL);
377 : 0 : g_queue_free (self->queue);
378 : 0 : self->queue = queue;
379 : :
380 : 0 : g_hash_table_destroy (paired);
381 : 0 : g_hash_table_destroy (pairs);
382 : : }
383 : :
384 : : static void
385 : 0 : complete_supplement (GTask *task,
386 : : GError *error)
387 : : {
388 : 0 : GcrImporterData *data = g_task_get_task_data (task);
389 : : GckAttributes *attributes;
390 : :
391 [ # # ]: 0 : if (error == NULL) {
392 : 0 : attributes = gck_builder_end (data->supplement);
393 : 0 : supplement_attributes (data->importer, attributes);
394 : 0 : gck_attributes_unref (attributes);
395 : :
396 : 0 : next_state (task, state_create_object);
397 : : } else {
398 : 0 : g_task_return_error (task, g_steal_pointer (&error));
399 : : }
400 : 0 : }
401 : :
402 : : static void
403 : 0 : on_supplement_done (GObject *source,
404 : : GAsyncResult *result,
405 : : gpointer user_data)
406 : : {
407 : 0 : GTask *task = G_TASK (user_data);
408 : 0 : GcrImporterData *data = g_task_get_task_data (task);
409 : 0 : GcrPkcs11Importer *self = data->importer;
410 : 0 : GError *error = NULL;
411 : :
412 : 0 : gcr_import_interaction_supplement_finish (GCR_IMPORT_INTERACTION (self->interaction),
413 : : result, &error);
414 : 0 : complete_supplement (task, error);
415 [ # # ]: 0 : g_clear_object (&task);
416 : 0 : }
417 : :
418 : : static void
419 : 0 : state_supplement (GTask *task,
420 : : gboolean async)
421 : : {
422 : 0 : GcrImporterData *data = g_task_get_task_data (task);
423 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
424 : 0 : GcrPkcs11Importer *self = data->importer;
425 : 0 : GError *error = NULL;
426 : :
427 [ # # # # ]: 0 : if (self->interaction == NULL || !GCR_IS_IMPORT_INTERACTION (self->interaction)) {
428 : 0 : complete_supplement (task, NULL);
429 : :
430 [ # # ]: 0 : } else if (async) {
431 : 0 : gcr_import_interaction_supplement_async (GCR_IMPORT_INTERACTION (self->interaction),
432 : : data->supplement, cancellable,
433 : : on_supplement_done,
434 : : g_object_ref (task));
435 : :
436 : : } else {
437 : 0 : gcr_import_interaction_supplement (GCR_IMPORT_INTERACTION (self->interaction),
438 : : data->supplement, cancellable, &error);
439 : 0 : complete_supplement (task, error);
440 : : }
441 : 0 : }
442 : :
443 : : static void
444 : 0 : supplement_prep (GTask *task)
445 : : {
446 : 0 : GcrImporterData *data = g_task_get_task_data (task);
447 : 0 : GcrPkcs11Importer *self = data->importer;
448 : 0 : const GckAttribute *the_label = NULL;
449 : : const GckAttribute *attr;
450 : 0 : gboolean first = TRUE;
451 : : GList *l;
452 : :
453 [ # # ]: 0 : if (data->supplement)
454 : 0 : gck_builder_unref (data->supplement);
455 : 0 : data->supplement = gck_builder_new (GCK_BUILDER_NONE);
456 : :
457 : : /* Do we have a consistent label across all objects? */
458 [ # # ]: 0 : for (l = self->queue->head; l != NULL; l = g_list_next (l)) {
459 : 0 : attr = gck_attributes_find (l->data, CKA_LABEL);
460 [ # # ]: 0 : if (first)
461 : 0 : the_label = attr;
462 [ # # ]: 0 : else if (!gck_attribute_equal (the_label, attr))
463 : 0 : the_label = NULL;
464 [ # # ]: 0 : first = FALSE;
465 : : }
466 : :
467 : : /* If consistent label, set that in supplement data */
468 [ # # ]: 0 : if (the_label != NULL)
469 : 0 : gck_builder_add_data (data->supplement, CKA_LABEL, the_label->value, the_label->length);
470 : : else
471 : 0 : gck_builder_add_empty (data->supplement, CKA_LABEL);
472 : :
473 [ # # ]: 0 : if (GCR_IS_IMPORT_INTERACTION (self->interaction))
474 : 0 : gcr_import_interaction_supplement_prep (GCR_IMPORT_INTERACTION (self->interaction),
475 : : data->supplement);
476 : 0 : }
477 : :
478 : : /* ---------------------------------------------------------------------------------
479 : : * OPEN SESSION
480 : : */
481 : :
482 : : static void
483 : 0 : complete_open_session (GTask *task,
484 : : GckSession *session,
485 : : GError *error)
486 : : {
487 : 0 : GcrImporterData *data = g_task_get_task_data (task);
488 : 0 : GcrPkcs11Importer *self = data->importer;
489 : :
490 [ # # ]: 0 : if (!session) {
491 : 0 : g_task_return_error (task, g_steal_pointer (&error));
492 : :
493 : : } else {
494 [ # # ]: 0 : g_clear_object (&self->session);
495 : 0 : self->session = session;
496 : 0 : next_state (task, state_supplement);
497 : : }
498 : 0 : }
499 : :
500 : : static void
501 : 0 : on_open_session (GObject *source,
502 : : GAsyncResult *result,
503 : : gpointer user_data)
504 : : {
505 : 0 : GTask *task = G_TASK (user_data);
506 : 0 : GError *error = NULL;
507 : : GckSession *session;
508 : :
509 : 0 : session = gck_session_open_finish (result, &error);
510 : 0 : complete_open_session (task, session, error);
511 [ # # ]: 0 : g_clear_object (&task);
512 : 0 : }
513 : :
514 : : static void
515 : 0 : state_open_session (GTask *task,
516 : : gboolean async)
517 : : {
518 : 0 : GcrImporterData *data = g_task_get_task_data (task);
519 : 0 : GCancellable *cancellable = g_task_get_cancellable (task);
520 : 0 : GcrPkcs11Importer *self = data->importer;
521 : 0 : guint options = GCK_SESSION_READ_WRITE | GCK_SESSION_LOGIN_USER;
522 : : GckSession *session;
523 : 0 : GError *error = NULL;
524 : :
525 [ # # ]: 0 : if (async) {
526 : 0 : gck_session_open_async (self->slot, options, self->interaction,
527 : : cancellable, on_open_session, g_object_ref (task));
528 : : } else {
529 : 0 : session = gck_session_open (self->slot, options, self->interaction,
530 : : cancellable, &error);
531 : 0 : complete_open_session (task, session, error);
532 : : }
533 : 0 : }
534 : :
535 : : static void
536 : 0 : _gcr_pkcs11_importer_init (GcrPkcs11Importer *self)
537 : : {
538 : 0 : self->queue = g_queue_new ();
539 : 0 : }
540 : :
541 : : static void
542 : 0 : _gcr_pkcs11_importer_dispose (GObject *obj)
543 : : {
544 : 0 : GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj);
545 : :
546 [ # # ]: 0 : g_clear_list (&self->objects, g_object_unref);
547 [ # # ]: 0 : g_clear_object (&self->session);
548 [ # # ]: 0 : g_clear_object (&self->interaction);
549 : :
550 [ # # ]: 0 : while (!g_queue_is_empty (self->queue))
551 : 0 : gck_attributes_unref (g_queue_pop_head (self->queue));
552 : :
553 : 0 : G_OBJECT_CLASS (_gcr_pkcs11_importer_parent_class)->dispose (obj);
554 : 0 : }
555 : :
556 : : static void
557 : 0 : _gcr_pkcs11_importer_finalize (GObject *obj)
558 : : {
559 : 0 : GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj);
560 : :
561 : 0 : g_queue_free (self->queue);
562 [ # # ]: 0 : g_clear_object (&self->slot);
563 : :
564 : 0 : G_OBJECT_CLASS (_gcr_pkcs11_importer_parent_class)->finalize (obj);
565 : 0 : }
566 : :
567 : : static void
568 : 0 : _gcr_pkcs11_importer_set_property (GObject *obj,
569 : : guint prop_id,
570 : : const GValue *value,
571 : : GParamSpec *pspec)
572 : : {
573 : 0 : GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj);
574 : :
575 [ # # # ]: 0 : switch (prop_id) {
576 : 0 : case PROP_SLOT:
577 : 0 : self->slot = g_value_dup_object (value);
578 [ # # ]: 0 : g_return_if_fail (self->slot);
579 : 0 : break;
580 : 0 : case PROP_INTERACTION:
581 [ # # ]: 0 : g_clear_object (&self->interaction);
582 : 0 : self->interaction = g_value_dup_object (value);
583 : 0 : g_object_notify (G_OBJECT (self), "interaction");
584 : 0 : break;
585 : 0 : default:
586 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
587 : 0 : break;
588 : : }
589 : : }
590 : :
591 : : static gchar *
592 : 0 : calculate_label (GcrPkcs11Importer *self)
593 : : {
594 : : GckTokenInfo *info;
595 : : gchar *result;
596 : :
597 : 0 : info = gck_slot_get_token_info (self->slot);
598 : 0 : result = g_strdup (info->label);
599 : 0 : gck_token_info_free (info);
600 : :
601 : 0 : return result;
602 : : }
603 : :
604 : : static gchar *
605 : 0 : calculate_uri (GcrPkcs11Importer *self)
606 : : {
607 : : GckUriData *data;
608 : : gchar *uri;
609 : :
610 : 0 : data = gck_uri_data_new ();
611 : 0 : data->token_info = gck_slot_get_token_info (self->slot);
612 : 0 : uri = gck_uri_data_build (data, GCK_URI_FOR_TOKEN);
613 : 0 : data->token_info = NULL;
614 : 0 : gck_uri_data_free (data);
615 : :
616 : 0 : return uri;
617 : : }
618 : :
619 : : static void
620 : 0 : _gcr_pkcs11_importer_get_property (GObject *obj,
621 : : guint prop_id,
622 : : GValue *value,
623 : : GParamSpec *pspec)
624 : : {
625 : 0 : GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (obj);
626 : :
627 [ # # # # : 0 : switch (prop_id) {
# # ]
628 : 0 : case PROP_LABEL:
629 : 0 : g_value_take_string (value, calculate_label (self));
630 : 0 : break;
631 : 0 : case PROP_SLOT:
632 : 0 : g_value_set_object (value, _gcr_pkcs11_importer_get_slot (self));
633 : 0 : break;
634 : 0 : case PROP_QUEUED:
635 : 0 : g_value_set_pointer (value, _gcr_pkcs11_importer_get_queued (self));
636 : 0 : break;
637 : 0 : case PROP_INTERACTION:
638 : 0 : g_value_set_object (value, self->interaction);
639 : 0 : break;
640 : 0 : case PROP_URI:
641 : 0 : g_value_take_string (value, calculate_uri (self));
642 : 0 : break;
643 : 0 : default:
644 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
645 : 0 : break;
646 : : }
647 : 0 : }
648 : :
649 : : static void
650 : 0 : _gcr_pkcs11_importer_class_init (GcrPkcs11ImporterClass *klass)
651 : : {
652 : 0 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
653 : 0 : GckBuilder builder = GCK_BUILDER_INIT;
654 : :
655 : 0 : gobject_class->dispose = _gcr_pkcs11_importer_dispose;
656 : 0 : gobject_class->finalize = _gcr_pkcs11_importer_finalize;
657 : 0 : gobject_class->set_property = _gcr_pkcs11_importer_set_property;
658 : 0 : gobject_class->get_property = _gcr_pkcs11_importer_get_property;
659 : :
660 : 0 : g_object_class_override_property (gobject_class, PROP_LABEL, "label");
661 : 0 : g_object_class_override_property (gobject_class, PROP_INTERACTION, "interaction");
662 : 0 : g_object_class_override_property (gobject_class, PROP_URI, "uri");
663 : :
664 : 0 : g_object_class_install_property (gobject_class, PROP_SLOT,
665 : : g_param_spec_object ("slot", "Slot", "PKCS#11 slot to import data into",
666 : : GCK_TYPE_SLOT,
667 : : G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
668 : :
669 : 0 : g_object_class_install_property (gobject_class, PROP_QUEUED,
670 : : g_param_spec_pointer ("queued", "Queued", "Queued attributes",
671 : : G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
672 : :
673 : 0 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_CERTIFICATE);
674 : 0 : gck_builder_add_ulong (&builder, CKA_CERTIFICATE_TYPE, CKC_X_509);
675 : 0 : gcr_importer_register (GCR_TYPE_PKCS11_IMPORTER, gck_builder_end (&builder));
676 : :
677 : 0 : gck_builder_add_ulong (&builder, CKA_CLASS, CKO_PRIVATE_KEY);
678 : 0 : gcr_importer_register (GCR_TYPE_PKCS11_IMPORTER, gck_builder_end (&builder));
679 : :
680 : 0 : _gcr_initialize_library ();
681 : 0 : }
682 : :
683 : : static GList *
684 : 0 : list_all_slots (void)
685 : : {
686 : : GList *modules;
687 : : GList *results;
688 : :
689 : 0 : modules = gcr_pkcs11_get_modules ();
690 : 0 : results = gck_modules_get_slots (modules, TRUE);
691 [ # # ]: 0 : g_clear_list (&modules, g_object_unref);
692 : :
693 : 0 : return results;
694 : : }
695 : :
696 : : static const char *token_blacklist[] = {
697 : : "pkcs11:manufacturer=Gnome%20Keyring;serial=1:SECRET:MAIN",
698 : : "pkcs11:manufacturer=Gnome%20Keyring;serial=1%3aXDG%3aDEFAULT",
699 : : NULL
700 : : };
701 : :
702 : : static gboolean
703 : 0 : is_slot_importable (GckSlot *slot,
704 : : GckTokenInfo *token)
705 : : {
706 : 0 : GError *error = NULL;
707 : : GckUriData *uri;
708 : : gboolean match;
709 : : guint i;
710 : :
711 [ # # ]: 0 : if (token->flags & CKF_WRITE_PROTECTED) {
712 : 0 : g_debug ("token is not importable: %s: write protected", token->label);
713 : 0 : return FALSE;
714 : : }
715 [ # # ]: 0 : if (!(token->flags & CKF_TOKEN_INITIALIZED)) {
716 : 0 : g_debug ("token is not importable: %s: not initialized", token->label);
717 : 0 : return FALSE;
718 : : }
719 [ # # ]: 0 : if ((token->flags & CKF_LOGIN_REQUIRED) &&
720 [ # # ]: 0 : !(token->flags & CKF_USER_PIN_INITIALIZED)) {
721 : 0 : g_debug ("token is not importable: %s: user pin not initialized", token->label);
722 : 0 : return FALSE;
723 : : }
724 : :
725 [ # # ]: 0 : for (i = 0; token_blacklist[i] != NULL; i++) {
726 : 0 : uri = gck_uri_data_parse (token_blacklist[i], GCK_URI_FOR_TOKEN | GCK_URI_FOR_MODULE, &error);
727 [ # # ]: 0 : if (uri == NULL) {
728 : 0 : g_warning ("couldn't parse pkcs11 blacklist uri: %s", error->message);
729 : 0 : g_clear_error (&error);
730 : 0 : continue;
731 : : }
732 : :
733 : 0 : match = gck_slot_match (slot, uri);
734 : 0 : gck_uri_data_free (uri);
735 : :
736 [ # # ]: 0 : if (match) {
737 : 0 : g_debug ("token is not importable: %s: on the black list", token->label);
738 : 0 : return FALSE;
739 : : }
740 : : }
741 : :
742 : 0 : return TRUE;
743 : : }
744 : :
745 : : static GList *
746 : 0 : _gcr_pkcs11_importer_create_for_parsed (GcrParsed *parsed)
747 : : {
748 : : GcrImporter *self;
749 : : GList *slots, *l;
750 : 0 : GList *results = NULL;
751 : : GckTokenInfo *token_info;
752 : : gboolean importable;
753 : :
754 : 0 : slots = list_all_slots ();
755 [ # # # # ]: 0 : for (l = slots; l != NULL; l = g_list_next (l)) {
756 : 0 : token_info = gck_slot_get_token_info (l->data);
757 : 0 : importable = is_slot_importable (l->data, token_info);
758 : :
759 [ # # ]: 0 : if (importable) {
760 : 0 : g_debug ("creating importer for token: %s", token_info->label);
761 : 0 : self = _gcr_pkcs11_importer_new (l->data);
762 [ # # ]: 0 : if (!gcr_importer_queue_for_parsed (self, parsed))
763 : 0 : g_assert_not_reached ();
764 : 0 : results = g_list_prepend (results, self);
765 : : }
766 : :
767 : 0 : gck_token_info_free (token_info);
768 : : }
769 [ # # ]: 0 : g_clear_list (&slots, g_object_unref);
770 : :
771 : 0 : return g_list_reverse (results);
772 : : }
773 : :
774 : : static gboolean
775 : 0 : _gcr_pkcs11_importer_queue_for_parsed (GcrImporter *importer,
776 : : GcrParsed *parsed)
777 : : {
778 : 0 : GcrPkcs11Importer *self = GCR_PKCS11_IMPORTER (importer);
779 : : GckAttributes *attrs;
780 : : const gchar *label;
781 : :
782 : 0 : attrs = gcr_parsed_get_attributes (parsed);
783 : 0 : label = gcr_parsed_get_label (parsed);
784 : 0 : _gcr_pkcs11_importer_queue (self, label, attrs);
785 : :
786 : 0 : return TRUE;
787 : : }
788 : :
789 : : static void
790 : 0 : _gcr_pkcs11_importer_import_async (GcrImporter *importer,
791 : : GCancellable *cancellable,
792 : : GAsyncReadyCallback callback,
793 : : gpointer user_data)
794 : : {
795 : : GTask *task;
796 : : GcrImporterData *data;
797 : :
798 : 0 : task = g_task_new (importer, cancellable, callback, user_data);
799 [ # # ]: 0 : g_task_set_source_tag (task, _gcr_pkcs11_importer_import_async);
800 : :
801 : 0 : data = g_new0 (GcrImporterData, 1);
802 : 0 : data->async = TRUE;
803 : 0 : data->importer = GCR_PKCS11_IMPORTER (g_object_ref (importer));
804 : 0 : g_task_set_task_data (task, data, gcr_importer_data_free);
805 : :
806 : 0 : supplement_prep (task);
807 : :
808 : 0 : next_state (task, state_open_session);
809 [ # # ]: 0 : g_clear_object (&task);
810 : 0 : }
811 : :
812 : : static gboolean
813 : 0 : _gcr_pkcs11_importer_import_finish (GcrImporter *importer,
814 : : GAsyncResult *result,
815 : : GError **error)
816 : : {
817 [ # # ]: 0 : g_return_val_if_fail (g_task_is_valid (result, importer), FALSE);
818 : :
819 : 0 : return g_task_propagate_boolean (G_TASK (result), error);
820 : : }
821 : :
822 : : static void
823 : 0 : _gcr_pkcs11_importer_init_iface (GcrImporterInterface *iface)
824 : : {
825 : 0 : iface->create_for_parsed = _gcr_pkcs11_importer_create_for_parsed;
826 : 0 : iface->queue_for_parsed = _gcr_pkcs11_importer_queue_for_parsed;
827 : 0 : iface->import_async = _gcr_pkcs11_importer_import_async;
828 : 0 : iface->import_finish = _gcr_pkcs11_importer_import_finish;
829 : 0 : }
830 : :
831 : : /**
832 : : * _gcr_pkcs11_importer_new:
833 : : *
834 : : * Create a new #GcrPkcs11Importer.
835 : : *
836 : : * Returns: (transfer full) (type Gcr.Pkcs11Importer): the new importer
837 : : */
838 : : GcrImporter *
839 : 0 : _gcr_pkcs11_importer_new (GckSlot *slot)
840 : : {
841 [ # # ]: 0 : g_return_val_if_fail (GCK_IS_SLOT (slot), NULL);
842 : :
843 : 0 : return g_object_new (GCR_TYPE_PKCS11_IMPORTER,
844 : : "slot", slot,
845 : : NULL);
846 : : }
847 : :
848 : : GckSlot *
849 : 0 : _gcr_pkcs11_importer_get_slot (GcrPkcs11Importer *self)
850 : : {
851 [ # # ]: 0 : g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL);
852 : 0 : return self->slot;
853 : : }
854 : :
855 : : GList *
856 : 0 : _gcr_pkcs11_importer_get_queued (GcrPkcs11Importer *self)
857 : : {
858 [ # # ]: 0 : g_return_val_if_fail (GCR_IS_PKCS11_IMPORTER (self), NULL);
859 : 0 : return g_list_copy (self->queue->head);
860 : : }
861 : :
862 : : void
863 : 0 : _gcr_pkcs11_importer_queue (GcrPkcs11Importer *self,
864 : : const gchar *label,
865 : : GckAttributes *attrs)
866 : : {
867 : 0 : GckBuilder builder = GCK_BUILDER_INIT;
868 : :
869 [ # # ]: 0 : g_return_if_fail (GCR_IS_PKCS11_IMPORTER (self));
870 [ # # ]: 0 : g_return_if_fail (attrs != NULL);
871 : :
872 [ # # # # ]: 0 : if (label != NULL && !gck_attributes_find (attrs, CKA_LABEL)) {
873 : 0 : gck_builder_add_all (&builder, attrs);
874 : 0 : gck_builder_add_string (&builder, CKA_LABEL, label);
875 : 0 : attrs = gck_builder_end (&builder);
876 : : } else {
877 : 0 : gck_attributes_ref (attrs);
878 : : }
879 : :
880 : 0 : g_queue_push_tail (self->queue, attrs);
881 : : }
|